JavaScript 39 🧬 Error handling — try/catch/finally/throw
try {
JSON.parse('{ invalid }');
} catch (err) {
console.log('Caught:', err.message);
} finally {
console.log('Done');
}
function divide(a, b) {
if (b === 0) throw new Error('Division by zero');
return a / b;
}
try {
console.log(divide(10, 2));
console.log(divide(10, 0));
} catch (err) {
console.log('Error:', err.message);
}
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
try {
throw new ValidationError('Invalid email');
} catch (err) {
if (err instanceof ValidationError) {
console.log('Validation:', err.message);
} else if (err instanceof Error) {
console.log('Generic:', err.message);
}
}
Errors are a normal part of every program — files go missing, networks drop, users type nonsense, and code has bugs. JavaScript gives you a structured way to throw, catch, and handle errors so a single failure doesn’t crash the whole app. The try / catch / finally block is the foundation of that system.
Key point: When something goes wrong, JavaScript throws an error. If nothing catches it, the error propagates up the call stack and eventually terminates the program. try / catch lets you intercept errors, decide what to do, and keep running.
a – What is an Error
An Error in JavaScript is an object, not a string. When something fails, an object is created with useful information.
Properties of an Error:
| Property | Meaning |
|---|---|
name | Type of error ("Error", "TypeError", "SyntaxError") |
message | Human-readable description |
stack | Stack trace showing where it happened |
cause | Optional underlying error (ES2022) |
Creating an error:
const err = new Error('Something went wrong');
console.log(err.name); // Error
console.log(err.message); // Something went wrong
console.log(err.stack); // multi-line stack trace
Built-in error types:
| Type | When it occurs |
|---|---|
Error | Generic base error |
TypeError | Wrong type — calling a non-function, reading undefined.foo |
ReferenceError | Using an undefined variable |
SyntaxError | Invalid code — usually caught at parse time |
RangeError | Number out of valid range |
URIError | Malformed URI |
EvalError | Legacy, rarely used |
Examples:
try {
undefinedFunction();
} catch (err) {
console.log(err.name); // ReferenceError
console.log(err.message); // undefinedFunction is not defined
}
try {
null.foo;
} catch (err) {
console.log(err.name); // TypeError
}
try {
new Array(-1);
} catch (err) {
console.log(err.name); // RangeError
}
Errors are objects, not strings:
// ❌ Old style — throws a string
throw 'something broke';
// ✅ Modern — throws an Error object
throw new Error('something broke');
Throwing strings is legal but bad practice — you lose .name, .stack, and type checking. Always throw Error objects.
Why errors matter:
- Fail loudly — silent failures are the worst bugs
- Preserve context —
.stacktells you where - Keep programs running — catch and recover
- Distinguish cases — custom error classes make handling precise
b – try, catch, and finally
The try / catch / finally structure is how you handle errors.
Syntax:
try {
// code that might throw
} catch (error) {
// runs if an error is thrown
} finally {
// always runs — success or failure
}
The try block:
Holds the code that might throw. If anything inside throws, execution jumps to catch.
try {
const data = JSON.parse('{ invalid }');
} catch (err) {
console.log('Caught:', err.message);
}
// Caught: Expected property name or '}' in JSON at position 2
The catch block:
Receives the error object. Runs only if something in try throws.
try {
riskyOperation();
} catch (error) {
console.error(error.name, error.message);
}
You can omit the binding in modern JS if you don’t need the error object:
try {
riskyOperation();
} catch {
console.log('Something failed');
}
The finally block:
Runs whether or not an error occurred. Used for cleanup — closing files, releasing locks, hiding spinners.
try {
showSpinner();
const data = await fetchData();
return data;
} catch (err) {
console.error('Failed:', err);
return null;
} finally {
hideSpinner(); // always runs
}
Execution flow:
┌──────────────────────────────────────────────┐
│ try / catch / finally │
│ │
│ try { │
│ // risky code │
│ ──── success ────► skip catch │
│ ──── throw ──────► run catch │
│ } │
│ │
│ catch (err) { │
│ // handle error │
│ } │
│ │
│ finally { │
│ // ALWAYS runs (cleanup) │
│ } │
│ │
└──────────────────────────────────────────────┘
finally runs even if try returns:
function test() {
try {
return 'from try';
} finally {
console.log('finally runs!');
}
}
console.log(test());
// [ finally runs! ]
// [ from try ]
The return in try doesn’t skip finally.
finally overrides try and catch returns:
function test() {
try {
return 'try';
} finally {
return 'finally';
}
}
console.log(test());
// [ finally ]
If finally has a return, it overrides everything. Avoid this — it’s confusing.
A practical example — file-like cleanup:
function processFile() {
console.log('Opening file');
try {
console.log('Processing...');
throw new Error('Corrupted data');
} catch (err) {
console.log('Error:', err.message);
} finally {
console.log('Closing file');
}
}
processFile();
// [ Opening file ]
// [ Processing... ]
// [ Error: Corrupted data ]
// [ Closing file ]
The file gets closed no matter what.
Full example:
try {
JSON.parse('{ invalid }');
} catch (err) {
console.log('Caught:', err.message);
} finally {
console.log('Done');
}
// [ Caught: Expected property name or '}' in JSON at position 2 ]
// [ Done ]
c – Throwing errors and custom error classes
You don’t have to wait for JavaScript to throw — you can throw errors yourself, and define your own error types.
Throwing errors with throw:
function divide(a, b) {
if (b === 0) {
throw new Error('Cannot divide by zero');
}
return a / b;
}
console.log(divide(10, 2));
// [ 5 ]
try {
divide(10, 0);
} catch (err) {
console.log(err.message);
}
// [ Cannot divide by zero ]
throw immediately stops execution of the current function and propagates the error up the stack.
Throwing values of any type:
throw new Error('real error'); // ✅ best practice
throw 'a string'; // ⚠️ works, but loses info
throw { code: 404 }; // ⚠️ works, but not an Error
throw 42; // ⚠️ works, but terrible
Only Error objects have .name, .message, .stack. Anything else is just a value.
Custom error classes — the modern pattern:
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
class NetworkError extends Error {
constructor(message, statusCode) {
super(message);
this.name = 'NetworkError';
this.statusCode = statusCode;
}
}
extends Error inherits everything. Setting this.name makes err.name correct (otherwise it would be 'Error').
Using custom errors:
try {
throw new ValidationError('Invalid email');
} catch (err) {
if (err instanceof ValidationError) {
console.log('Validation problem:', err.message);
} else if (err instanceof NetworkError) {
console.log('Network problem:', err.message);
} else if (err instanceof Error) {
console.log('Unknown error:', err.message);
}
}
// [ Validation problem: Invalid email ]
Order matters — check the most specific class first, then the base.
Adding extra properties:
class HTTPError extends Error {
constructor(message, status) {
super(message);
this.name = 'HTTPError';
this.status = status;
}
}
try {
throw new HTTPError('Not found', 404);
} catch (err) {
if (err instanceof HTTPError) {
console.log(`${err.status}: ${err.message}`);
}
}
// [ 404: Not found ]
Error cause — chaining (ES2022):
try {
JSON.parse('{ bad }');
} catch (err) {
throw new Error('Config load failed', { cause: err });
}
The cause option preserves the original error:
try {
throw new Error('Outer', { cause: new Error('Inner') });
} catch (err) {
console.log(err.message); // Outer
console.log(err.cause.message); // Inner
}
Useful for wrapping low-level errors with higher-level context.
Re-throwing errors:
Sometimes you want to log an error but not swallow it:
function loadData() {
try {
return JSON.parse(someInput);
} catch (err) {
console.error('Debug: failed to parse', err);
throw err; // re-throw — let the caller decide
}
}
Full example:
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
try {
throw new ValidationError('Invalid email');
} catch (err) {
if (err instanceof ValidationError) {
console.log('Validation:', err.message);
} else if (err instanceof Error) {
console.log('Generic:', err.message);
}
}
// [ Validation: Invalid email ]
Complete Example Session
// ============================================
// PART 1: BASIC ERROR OBJECT
// ============================================
const err = new Error('Something went wrong');
console.log(err.name);
// [ Error ]
console.log(err.message);
// [ Something went wrong ]
console.log(typeof err.stack);
// [ string ]
// ============================================
// PART 2: BUILT-IN ERROR TYPES
// ============================================
try { undefinedFunction(); }
catch (e) { console.log(e.name); }
// [ ReferenceError ]
try { null.foo; }
catch (e) { console.log(e.name); }
// [ TypeError ]
try { new Array(-1); }
catch (e) { console.log(e.name); }
// [ RangeError ]
// ============================================
// PART 3: TRY / CATCH
// ============================================
try {
JSON.parse('{ invalid }');
} catch (err) {
console.log('Caught:', err.message);
}
// [ Caught: Expected property name or '}' in JSON at position 2 ]
// ============================================
// PART 4: TRY / CATCH / FINALLY
// ============================================
try {
JSON.parse('{ invalid }');
} catch (err) {
console.log('Caught:', err.message);
} finally {
console.log('Done');
}
// [ Caught: Expected property name or '}' in JSON at position 2 ]
// [ Done ]
// ============================================
// PART 5: FINALLY RUNS ON SUCCESS TOO
// ============================================
try {
JSON.parse('{"ok": true}');
} catch (err) {
console.log('Never runs');
} finally {
console.log('Always runs');
}
// [ Always runs ]
// ============================================
// PART 6: THROW
// ============================================
function divide(a, b) {
if (b === 0) {
throw new Error('Cannot divide by zero');
}
return a / b;
}
console.log(divide(10, 2));
// [ 5 ]
try {
divide(10, 0);
} catch (err) {
console.log('Error:', err.message);
}
// [ Error: Cannot divide by zero ]
// ============================================
// PART 7: FINALLY WITH RETURN
// ============================================
function test1() {
try {
return 'from try';
} finally {
console.log('finally runs');
}
}
console.log(test1());
// [ finally runs ]
// [ from try ]
// ============================================
// PART 8: FINALLY OVERRIDES RETURN
// ============================================
function test2() {
try {
return 'try';
} finally {
return 'finally';
}
}
console.log(test2());
// [ finally ]
// ============================================
// PART 9: CUSTOM ERROR CLASS
// ============================================
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
try {
throw new ValidationError('Invalid email');
} catch (err) {
console.log(err.name);
// [ ValidationError ]
console.log(err.message);
// [ Invalid email ]
}
// ============================================
// PART 10: MULTIPLE CUSTOM ERRORS
// ============================================
class NetworkError extends Error {
constructor(message, statusCode) {
super(message);
this.name = 'NetworkError';
this.statusCode = statusCode;
}
}
function handle(err) {
if (err instanceof ValidationError) return 'validation';
if (err instanceof NetworkError) return 'network';
if (err instanceof Error) return 'generic';
return 'unknown';
}
console.log(handle(new ValidationError('x')));
// [ validation ]
console.log(handle(new NetworkError('y', 500)));
// [ network ]
console.log(handle(new Error('z')));
// [ generic ]
// ============================================
// PART 11: ORDER MATTERS
// ============================================
class DogError extends Error {}
class AnimalError extends Error {}
try {
throw new DogError('woof');
} catch (err) {
if (err instanceof AnimalError) {
console.log('animal');
} else if (err instanceof DogError) {
console.log('dog'); // never reached
}
}
// (nothing printed)
// ============================================
// PART 12: ERROR CAUSE (ES2022)
// ============================================
try {
throw new Error('Outer', { cause: new Error('Inner') });
} catch (err) {
console.log(err.message);
// [ Outer ]
console.log(err.cause.message);
// [ Inner ]
}
// ============================================
// PART 13: RE-THROW
// ============================================
function loadData() {
try {
return JSON.parse('{ bad }');
} catch (err) {
console.error('Failed to parse');
throw err;
}
}
try {
loadData();
} catch (err) {
console.log('Outer caught:', err.name);
}
// [ Failed to parse ]
// [ Outer caught: SyntaxError ]
// ============================================
// PART 14: CATCH WITHOUT BINDING
// ============================================
try {
JSON.parse('{ bad }');
} catch {
console.log('Failed');
}
// [ Failed ]
// ============================================
// PART 15: VALIDATION
// ============================================
function setAge(age) {
if (typeof age !== 'number') {
throw new TypeError('Age must be a number');
}
if (age < 0 || age > 150) {
throw new RangeError('Age out of range');
}
return age;
}
try {
setAge('30');
} catch (err) {
console.log(err.name, '-', err.message);
}
// [ TypeError - Age must be a number ]
try {
setAge(200);
} catch (err) {
console.log(err.name, '-', err.message);
}
// [ RangeError - Age out of range ]
// ============================================
// PART 16: ASYNC ERRORS WITH TRY/CATCH
// ============================================
async function fetchData() {
throw new Error('Network failed');
}
async function main() {
try {
await fetchData();
} catch (err) {
console.log('Async caught:', err.message);
}
}
main();
// [ Async caught: Network failed ]
// ============================================
// PART 17: CLEANUP PATTERN
// ============================================
function useResource() {
console.log('Acquire');
try {
console.log('Use');
throw new Error('boom');
} catch (err) {
console.log('Handle:', err.message);
} finally {
console.log('Release');
}
}
useResource();
// [ Acquire ]
// [ Use ]
// [ Handle: boom ]
// [ Release ]
// ============================================
// PART 18: NESTED TRY/CATCH
// ============================================
try {
try {
throw new Error('inner');
} catch (err) {
console.log('Inner caught');
throw new Error('outer');
} finally {
console.log('Inner finally');
}
} catch (err) {
console.log('Outer caught:', err.message);
}
// [ Inner caught ]
// [ Inner finally ]
// [ Outer caught: outer ]
// ============================================
// PART 19: CUSTOM ERROR WITH PROPERTIES
// ============================================
class HTTPError extends Error {
constructor(message, status) {
super(message);
this.name = 'HTTPError';
this.status = status;
}
}
try {
throw new HTTPError('Not Found', 404);
} catch (err) {
console.log(`${err.status}: ${err.message}`);
}
// [ 404: Not Found ]
// ============================================
// PART 20: FULL SCRIPT
// ============================================
try {
JSON.parse('{ invalid }');
} catch (err) {
console.log('Caught:', err.message);
} finally {
console.log('Done');
}
function divide(a, b) {
if (b === 0) throw new Error('Division by zero');
return a / b;
}
try {
console.log(divide(10, 2));
console.log(divide(10, 0));
} catch (err) {
console.log('Error:', err.message);
}
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
try {
throw new ValidationError('Invalid email');
} catch (err) {
if (err instanceof ValidationError) {
console.log('Validation:', err.message);
} else if (err instanceof Error) {
console.log('Generic:', err.message);
}
}
Quick Reference
try / catch / finally
| Block | Runs when |
|---|---|
try | Always — the risky code |
catch | Only if try throws |
finally | Always — success or failure |
Error Properties
| Property | Meaning |
|---|---|
name | Type of error |
message | Description |
stack | Stack trace |
cause | Underlying error (ES2022) |
Built-in Error Types
| Type | Typical cause |
|---|---|
Error | Base class |
TypeError | Wrong type |
ReferenceError | Undefined variable |
SyntaxError | Invalid syntax |
RangeError | Out of range |
URIError | Bad URI |
Throwing
| Syntax | Meaning |
|---|---|
throw new Error('msg') | Throw a generic error |
throw new TypeError('msg') | Throw a specific error |
throw new MyError('msg') | Throw a custom error |
throw err | Re-throw |
Custom Errors
| Step | Code |
|---|---|
| Extend Error | class MyError extends Error {} |
| Call super | super(message) |
| Set name | this.name = 'MyError' |
| Add props | this.code = code |
Error Handling Patterns
| Pattern | Use |
|---|---|
try/catch | Handle errors locally |
try/finally | Cleanup only |
try/catch/finally | Handle + cleanup |
catch { } | Ignore error object |
| Re-throw | Log and re-throw |
Best Practices
✅ Do This:
// Throw Error objects
throw new Error('message'); // ✅
// Use specific error types
throw new TypeError('must be a number'); // ✅
// Extend Error for custom errors
class MyError extends Error {} // ✅
// Set this.name in custom errors
this.name = 'MyError'; // ✅
// Check specific before general
if (err instanceof MyError) { ... }
else if (err instanceof Error) { ... } // ✅
// Use finally for cleanup
try { ... } finally { cleanup(); } // ✅
// Chain errors with cause
throw new Error('Outer', { cause: err }); // ✅
// Re-throw when you can't handle
catch (err) { log(err); throw err; } // ✅
// Omit catch binding when unused
try { ... } catch { ... } // ✅
❌ Don’t Do This:
// Don't throw strings
throw 'error'; // ❌
// Don't throw numbers
throw 42; // ❌
// Don't swallow errors silently
try { ... } catch (err) {} // ❌
// Don't return from finally
finally { return 'x'; } // ❌ overrides
// Don't check general before specific
if (err instanceof Error) { ... }
else if (err instanceof TypeError) { ... } // ❌ unreachable
// Don't catch and ignore
catch (err) { /* nothing */ } // ❌
// Don't use try/catch for control flow
try { parse(x) } catch { fallback() } // ⚠️ overuse
// Don't forget this.name in custom errors
class E extends Error {} // ⚠️ err.name === 'Error'
// Don't use errors for expected conditions
try { findUser() } catch { ... } // ⚠️ return null instead
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
| Throwing strings | No .name/.stack | throw new Error(...) |
Missing this.name | err.name === 'Error' | Set it in the constructor |
| General before specific | Branch unreachable | Check specific first |
| Silent catch | Bugs disappear | Log or re-throw |
return in finally | Overrides try return | Avoid |
finally without cleanup | Pointless | Use only for cleanup |
| Catch for control flow | Slow, hides bugs | Use return values |
| Async error not caught | Unhandled rejection | await inside try |
Real-World Examples
1. Parse JSON Safely
try {
const data = JSON.parse('{ invalid }');
} catch (err) {
console.log('Parse failed:', err.message);
}
// [ Parse failed: Expected property name or '}' in JSON at position 2 ]
2. Divide with Guard
function divide(a, b) {
if (b === 0) throw new Error('Division by zero');
return a / b;
}
try {
console.log(divide(10, 0));
} catch (err) {
console.log(err.message);
}
// [ Division by zero ]
3. Validate Input
function setAge(age) {
if (typeof age !== 'number') throw new TypeError('Age must be a number');
if (age < 0 || age > 150) throw new RangeError('Age out of range');
return age;
}
try {
setAge('30');
} catch (err) {
console.log(err.name, '-', err.message);
}
// [ TypeError - Age must be a number ]
4. Custom ValidationError
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
try {
throw new ValidationError('Invalid email');
} catch (err) {
console.log(err.name);
// [ ValidationError ]
}
5. HTTP Error with Status
class HTTPError extends Error {
constructor(message, status) {
super(message);
this.name = 'HTTPError';
this.status = status;
}
}
try {
throw new HTTPError('Not Found', 404);
} catch (err) {
console.log(`${err.status}: ${err.message}`);
}
// [ 404: Not Found ]
6. Cleanup with finally
function useResource() {
console.log('Acquire');
try {
console.log('Use');
throw new Error('boom');
} catch (err) {
console.log('Handle:', err.message);
} finally {
console.log('Release');
}
}
useResource();
// [ Acquire ]
// [ Use ]
// [ Handle: boom ]
// [ Release ]
7. Async with try/catch
async function fetchData() {
throw new Error('Network failed');
}
async function main() {
try {
await fetchData();
} catch (err) {
console.log('Async caught:', err.message);
}
}
main();
// [ Async caught: Network failed ]
8. Error Cause
try {
throw new Error('Config failed', { cause: new Error('File missing') });
} catch (err) {
console.log(err.message); // Config failed
console.log(err.cause.message); // File missing
}
9. Re-throw
function load() {
try {
return JSON.parse('{ bad }');
} catch (err) {
console.error('Parse failed');
throw err;
}
}
try {
load();
} catch (err) {
console.log('Outer:', err.name);
}
// [ Parse failed ]
// [ Outer: SyntaxError ]
10. Retry Logic
async function retry(fn, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (err) {
if (i === attempts - 1) throw err;
console.log(`Attempt ${i + 1} failed, retrying...`);
}
}
}
retry(async () => {
throw new Error('always fails');
}, 2).catch(err => console.log('Gave up:', err.message));
// [ Attempt 1 failed, retrying... ]
// [ Gave up: always fails ]
11. Fallback Value
function parseConfig(text) {
try {
return JSON.parse(text);
} catch {
return {};
}
}
console.log(parseConfig('{ bad }'));
// [ {} ]
12. Distinguish Error Types
function handle(err) {
if (err instanceof TypeError) return 'type';
if (err instanceof RangeError) return 'range';
if (err instanceof Error) return 'generic';
return 'unknown';
}
console.log(handle(new TypeError('x')));
// [ type ]
console.log(handle(new Error('y')));
// [ generic ]
13. Nested try
try {
try {
throw new Error('inner');
} catch (err) {
console.log('Inner caught');
throw new Error('outer');
} finally {
console.log('Inner finally');
}
} catch (err) {
console.log('Outer caught:', err.message);
}
// [ Inner caught ]
// [ Inner finally ]
// [ Outer caught: outer ]
14. Assert-style Check
function assert(condition, message) {
if (!condition) throw new Error(message);
}
try {
assert(1 === 2, 'Math is broken');
} catch (err) {
console.log(err.message);
}
// [ Math is broken ]
15. Timeout Wrapper
function withTimeout(promise, ms) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
)
]);
}
16. Graceful Degradation
function loadUser(id) {
try {
return api.fetchUser(id);
} catch (err) {
console.error('Falling back to cache');
return cache.get(id);
}
}
17. Error Logging
function logError(err) {
console.error(`[${new Date().toISOString()}] ${err.name}: ${err.message}`);
if (err.stack) console.error(err.stack);
}
try {
JSON.parse('{ bad }');
} catch (err) {
logError(err);
}
18. Try/Finally Without Catch
function cleanup() {
console.log('cleanup');
}
try {
console.log('work');
} finally {
cleanup();
}
// [ work ]
// [ cleanup ]
19. Error in Finally
try {
throw new Error('original');
} finally {
throw new Error('from finally');
}
// Uncaught Error: from finally
The error thrown in finally replaces the original.
20. Full Script
try {
JSON.parse('{ invalid }');
} catch (err) {
console.log('Caught:', err.message);
} finally {
console.log('Done');
}
function divide(a, b) {
if (b === 0) throw new Error('Division by zero');
return a / b;
}
try {
console.log(divide(10, 2));
console.log(divide(10, 0));
} catch (err) {
console.log('Error:', err.message);
}
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
try {
throw new ValidationError('Invalid email');
} catch (err) {
if (err instanceof ValidationError) {
console.log('Validation:', err.message);
} else if (err instanceof Error) {
console.log('Generic:', err.message);
}
}
Visual: Error Flow
┌──────────────────────────────────────────────┐
│ Normal execution │
│ │
│ try │
│ │ │
│ ▼ │
│ statement 1 ✓ │
│ statement 2 ✓ │
│ statement 3 ✓ │
│ │ │
│ ▼ │
│ finally │
│ │
└──────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ Error thrown │
│ │
│ try │
│ │ │
│ ▼ │
│ statement 1 ✓ │
│ statement 2 ✗ throw │
│ │ │
│ ├──► catch (err) │
│ │ handle error │
│ │ │
│ ▼ │
│ finally │
│ │
└──────────────────────────────────────────────┘
Visual: try / catch / finally
┌──────────────────────────────────────────────┐
│ ┌────────────────────────────────────────┐ │
│ │ try │ │
│ │ // risky code │ │
│ └───────────────┬────────────────────────┘ │
│ │ │
│ ┌────────┴────────┐ │
│ success throw │
│ │ │ │
│ │ ▼ │
│ │ ┌───────────────────────┐ │
│ │ │ catch (err) │ │
│ │ │ // handle │ │
│ │ └───────────┬───────────┘ │
│ │ │ │
│ └────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ finally │ │
│ │ // always runs │ │
│ └─────────────────────────┘ │
│ │
└──────────────────────────────────────────────┘
Visual: Error Class Hierarchy
┌──────────────────────────────────────────────┐
│ Error │
│ │ │
│ ├── TypeError │
│ ├── ReferenceError │
│ ├── SyntaxError │
│ ├── RangeError │
│ ├── URIError │
│ └── (your custom errors) │
│ │ │
│ ├── ValidationError │
│ ├── NetworkError │
│ └── HTTPError │
│ │
└──────────────────────────────────────────────┘
Summary
| Concept | Syntax | Example |
|---|---|---|
| Throw | throw new Error('msg') | throw new Error('fail') |
| Try | try { ... } | try { risky() } |
| Catch | catch (err) { ... } | catch (err) { log(err) } |
| Finally | finally { ... } | finally { cleanup() } |
| Catch no binding | catch { ... } | catch { log('fail') } |
| Error props | err.name, err.message | err.message |
| Stack | err.stack | Print for debug |
| Cause | { cause: err } | new Error('x', { cause: e }) |
| Custom error | class E extends Error | class MyError extends Error |
| Set name | this.name = 'MyError' | In constructor |
| Type check | err instanceof TypeError | Specific before general |
| Re-throw | throw err | In catch |
| Built-ins | Error, TypeError, RangeError | throw new TypeError() |
Key takeaways:
Erroris an object — always throwErrorinstances, never stringstryholds risky code;catchhandles errors;finallyalways runsfinallyis for cleanup — closing files, releasing resources, hiding UIfinallyruns even whentryreturns — and itsreturnoverrides- Built-in errors:
Error,TypeError,ReferenceError,SyntaxError,RangeError,URIError - Custom errors extend
Error, callsuper(message), and setthis.name - Check specific before general —
instanceof MyErrorbeforeinstanceof Error - Use
{ cause: err }to chain errors with context - Re-throw with
throw errwhen you can’t handle — never swallow silently - Async errors inside
awaitare caught bytry/catchjust like sync errors - Errors are for exceptional cases — not for control flow
Remember: Errors are normal. Handle them with try/catch/finally, throw Error objects (never strings), and use custom error classes for precision. Always clean up in finally. Re-throw when you can’t recover. Check the specific error type before the general one. And never swallow errors silently — that’s how bugs hide. Master error handling, and your programs fail gracefully instead of crashing.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!