Suggest an editImprove this articleRefine the answer for “Errors and error handling”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Synchronous errors are caught with a `try/catch/finally` block and raised with `throw new Error(...)`; asynchronous ones are caught either by the same `try/catch` around `await` or by a `.catch()` on the promise.** For domain cases you create custom classes that extend `Error` and pass the original error through `{ cause }`. The last line of defence is the global handlers: `error` and `unhandledrejection` in the browser, `uncaughtException` and `unhandledRejection` in Node.js. ```javascript try { risky(); } catch (err) { console.error(err.message); } finally { cleanup(); // always runs } ``` **Key point:** handle an error locally where you can actually do something about it, and keep the global handlers as a logging safety net, not as a replacement for local handling.Shown above the full answer for quick recall.Answer (EN)Image**Error handling in JavaScript works on three levels: a local `try/catch/finally` around risky code, rejection handling for asynchronous operations, and global handlers as the last line of defence.** An error is raised with the `throw` operator, and for domain scenarios you describe custom classes that extend `Error`. ## Theory ### TL;DR - `try/catch/finally` catches synchronous exceptions; `finally` always runs. - An error is raised with `throw new Error('...')`, and you should throw an `Error` object, not a string. - Custom classes (`class ValidationError extends Error`) give typed domain errors; `{ cause }` preserves the original reason. - Plain `try/catch` works with `async/await`; promise chains use `.catch()`. - `Promise.allSettled` keeps one failed operation from killing the whole batch. - Global handlers: `window.addEventListener('error' | 'unhandledrejection')` in the browser, `process.on('uncaughtException' | 'unhandledRejection')` in Node.js. ### Quick example ```javascript try { risky(); // code that may fail } catch (err) { console.error(err.message); // handling or logging } finally { cleanup(); // always runs (even on an error) } ``` Raising an error: ```javascript if (!user) { throw new Error('User not found'); } ``` ### Custom error classes Extending `Error` lets you tell domain errors apart from technical ones and handle them selectively with `instanceof`. ```javascript class ValidationError extends Error { constructor(message, cause) { super(message, { cause }); // Node 16+ and modern browsers: err.cause this.name = 'ValidationError'; } } throw new ValidationError('Invalid email'); ``` The `cause` field keeps the original error, so the low-level reason is not lost when you wrap it into something the user can understand. ### Asynchronous code **a) `async/await` with `try/catch`** ```javascript async function load() { try { const res = await fetch('/api/data'); if (!res.ok) throw new Error(`HTTP ${res.status}`); return await res.json(); } catch (err) { // handling, retry or notification console.error('Load failed:', err); return null; // or rethrow it: throw err } } ``` Note that `fetch` does not throw on an HTTP status of 404 or 500, so the status is checked by hand through `res.ok`. **b) Promises: `.catch()`** ```javascript fetch('/api/data') .then(r => r.json()) .catch(err => console.error('Request failed', err)); ``` **c) Parallel operations without failing the whole batch** ```javascript const results = await Promise.allSettled(urls.map(u => fetch(u))); for (const r of results) { if (r.status === 'fulfilled') console.log('OK', r.value); else console.warn('Failed', r.reason); } ``` `Promise.all` rejects on the very first failure, while `Promise.allSettled` waits for all of them and reports the status of each operation separately. ### Global handlers (the last line of defence) **Browser** ```javascript // synchronous and uncaught exceptions window.addEventListener('error', (event) => { console.error('Uncaught error:', event.error, event.message, event.filename, event.lineno); }); // uncaught promise rejections window.addEventListener('unhandledrejection', (event) => { console.error('Unhandled rejection:', event.reason); }); ``` **Node.js** ```javascript process.on('uncaughtException', (err) => { console.error('Uncaught Exception', err); // logging and a clean shutdown of the process process.exit(1); }); process.on('unhandledRejection', (reason) => { console.error('Unhandled Rejection', reason); // the decision depends on the application policy: log and exit, or carry on }); ``` > Global handlers are a safety net, not a replacement for local handling. After an `uncaughtException` the process is in an undefined state, so the recommended practice is to log, close resources cleanly and exit, leaving the restart to a process manager. ### Common mistakes - **An empty `catch`.** A block that silently swallows the exception turns a bug into invisible behaviour; log it at the very least. - **`throw 'text'` instead of `throw new Error('text')`.** A string has neither `stack` nor `name`, so the stack trace is lost. - **A missing `await` before a call inside `try`.** The promise rejects after the block has already been left, and `catch` never sees it. - **Expecting `fetch` to throw on a 500.** It only rejects on a network failure; the status is checked with `res.ok`. - **A `try/catch` around an asynchronous callback.** An exception inside `setTimeout(() => { ... })` escapes in a different event loop tick and the outer block does not catch it. - **A global handler instead of a local one.** It is fine for telemetry, but it gives the user no meaningful message and does not restore the application state.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.