Exception handling in JavaScript is used to manage runtime errors without stopping the entire program. It helps developers write stable and reliable applications.
What is an Exception?
An exception is an unexpected error that occurs while the program is running.
Examples:
- Dividing by invalid values
- Accessing undefined variables
- Invalid JSON parsing
- Network/API failures
- Custom validation errors
Why Exception Handling is Important
Exception handling helps to:
- Prevent application crashes
- Show meaningful error messages
- Debug applications easily
- Handle API/database failures gracefully
- Improve user experience
1. The Core Structure: try...catch
The try block contains code that might throw an error. If an error occurs, JavaScript immediately stops executing the try block and jumps to the catch block.
JavaScript
try {
// Code that might fail
let result = 10 / unknownVariable;
console.log("This line will not run");
} catch (error) {
// Code to handle the error
console.log("An error occurred:", error.message);
}
console.log("Execution continues normally.");
2. Standard Error Properties
Inside the catch block, the error object provides valuable diagnostic metadata:
error.name: The type of error (e.g.,ReferenceError,TypeError).error.message: A human-readable description of what went wrong.error.stack: The stack trace showing where the error occurred in the source files.
JavaScript
try {
null.toString(); // Throws a TypeError
} catch (err) {
console.log("Name:", err.name); // "TypeError"
console.log("Message:", err.message); // "Cannot read properties of null..."
}
3. The finally Block
The optional finally block always executes, regardless of whether an error occurred or was caught. It is ideal for cleanup tasks (e.g., closing file streams, resetting UI states, or clearing timers).
JavaScript
let isLoading = true;
try {
// Simulating an operation
console.log("Fetching data...");
throw new Error("Network timeout");
} catch (err) {
console.log("Error caught:", err.message);
} finally {
// Runs whether try succeeds or catch fires
isLoading = false;
console.log("Loading completed. Status:", isLoading);
}
4. Throwing Custom Errors (throw)
You can trigger explicit errors using the throw statement. While you can throw primitives (strings or numbers), it is best practice to throw standard Error objects so you retain stack trace information.
JavaScript
function withdrawMoney(amount, balance) {
if (typeof amount !== 'number') {
throw new TypeError("Amount must be a number");
}
if (amount > balance) {
throw new Error("Insufficient funds for this withdrawal");
}
return balance - amount;
}
try {
let newBalance = withdrawMoney(150, 100);
} catch (err) {
console.log(`[${err.name}] ${err.message}`);
}
5. Built-in JavaScript Error Types
Understanding common built-in error classes helps you diagnose issues quickly:
| Error Type | Trigger Cause | Example |
ReferenceError | Accessing an undeclared variable | console.log(x); |
TypeError | Performing an invalid operation on a value | "text".toUpperCase() works, but (123).toUpperCase() fails |
SyntaxError | Invalid JavaScript syntax (usually caught at parse time) | JSON.parse("{ bad json }") |
RangeError | A numeric variable or parameter is outside its valid range | new Array(-1) |
6. Asynchronous Exception Handling
Traditional try...catch blocks cannot catch errors inside asynchronous callbacks or promises because the try block finishes executing before the async callback runs.
Promises
Use the .catch() method to handle rejected promises:
JavaScript
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("API server offline")), 1000);
});
}
fetchData()
.then(data => console.log(data))
.catch(err => console.log("Promise Error:", err.message));
async / await
With async / await, you can wrap asynchronous code in a standard try...catch block:
JavaScript
async function loadUserData() {
try {
let response = await fetchData(); // Waits for promise resolution/rejection
console.log(response);
} catch (err) {
console.log("Async/Await Error:", err.message);
}
}
loadUserData();
Nested try…catch
You can use multiple try-catch blocks.
try {
try {
console.log(userName);
} catch(error) {
console.log("Inner Catch");
}
console.log(data);
} catch(error) {
console.log("Outer Catch");
}
Exception Handling in Functions
function divide(a, b) {
try {
if(b === 0) {
throw new Error("Division by zero not allowed");
}
return a / b;
} catch(error) {
return error.message;
}
}
console.log(divide(10, 0));
Exception Handling with JSON
Very common in API automation and backend development.
let jsonData = '{"name":"Deepesh"}';
try {
let user = JSON.parse(jsonData);
console.log(user.name);
} catch(error) {
console.log("Invalid JSON");
}
Invalid JSON Example
let jsonData = '{"name":"Deepesh"';
try {
let user = JSON.parse(jsonData);
} catch(error) {
console.log("JSON Parsing Failed");
}
Async Exception Handling
Used with async/await.
Example
async function fetchData() {
try {
let response = await fetch("https://dummyjson.com/users/1");
let data = await response.json();
console.log(data);
} catch(error) {
console.log("API Error:", error.message);
}
}
fetchData();
Type of Errors with Examples:
JavaScript provides standard built-in error constructors that inherit from the base Error object. Each error type corresponds to a specific category of runtime exception.
1. Core Built-In Error Types
Error (Generic Base Error)
The generic base object used for general exceptions or as a parent class for custom user-defined errors.
JavaScript
try {
throw new Error("Application failed to process the request.");
} catch (err) {
console.log(err.name); // "Error"
console.log(err.message); // "Application failed to process the request."
}
TypeError
Occurs when an operation is performed on a value of an unexpected data type, such as attempting to invoke a non-function or reading properties of null/undefined.
JavaScript
try {
const count = 42;
count.toUpperCase(); // TypeError: count.toUpperCase is not a function
} catch (err) {
console.log(err.name); // "TypeError"
}
ReferenceError
Occurs when trying to access or dereference a variable that does not exist or has not been initialized (such as accessing a let/const variable in its Temporal Dead Zone).
JavaScript
try {
console.log(nonExistentVariable); // ReferenceError
} catch (err) {
console.log(err.name); // "ReferenceError"
}
SyntaxError
Occurs when code violates JavaScript language syntax rules. Parsing utilities like JSON.parse() or eval() throw runtime SyntaxErrors when passed malformed input.
JavaScript
try {
JSON.parse("{ badJson: true "); // SyntaxError: Unexpected token...
} catch (err) {
console.log(err.name); // "SyntaxError"
}
RangeError
Occurs when a numeric value or function argument falls outside its allowed range or bounds (e.g., invalid array lengths, out-of-bounds numbers in .toFixed(), or max stack size recursion).
JavaScript
try {
const invalidArray = new Array(-10); // RangeError: Invalid array length
} catch (err) {
console.log(err.name); // "RangeError"
}
URIError
Occurs when URI encoding or decoding global functions (such as decodeURI(), decodeURIComponent(), or encodeURI()) encounter malformed characters.
JavaScript
try {
decodeURI("%"); // URIError: URI malformed
} catch (err) {
console.log(err.name); // "URIError"
}
AggregateError
Introduced in ES2021, AggregateError wraps multiple errors into a single error object. It is automatically thrown by Promise.any() when every promise passed to it rejects.
JavaScript
Promise.any([
Promise.reject(new Error("Database offline")),
Promise.reject(new Error("Cache unavailable"))
]).catch(err => {
console.log(err instanceof AggregateError); // true
console.log(err.errors[0].message); // "Database offline"
console.log(err.errors[1].message); // "Cache unavailable"
});
SuppressedError
Introduced with Explicit Resource Management (using declarations). It occurs when an error is thrown during the cleanup/disposal of a resource while another error was already active.
JavaScript
try {
// Represents a primary error and a secondary disposal error
throw new SuppressedError(
new Error("Cleanup failed"), // The new error during disposal
new Error("Main logic failed"), // The original suppressed error
"Resource disposal failure"
);
} catch (err) {
console.log(err.error.message); // "Cleanup failed"
console.log(err.suppressed.message); // "Main logic failed"
}
EvalError
A legacy error object created for issues related to the global eval() function. While modern JS engines no longer throw native EvalErrors (favoring SyntaxError or TypeError instead), the constructor remains in the language specification for backwards compatibility.
JavaScript
try {
throw new EvalError("Custom evaluation error");
} catch (err) {
console.log(err.name); // "EvalError"
}
2. Environment & Host-Specific Errors
Beyond core ECMAScript, JavaScript runtime environments (browsers, Node.js) define additional specialized error objects:
| Error Type | Environment | Cause |
DOMException | Web APIs / Node.js | Raised by Web APIs for operations like aborted fetch requests (AbortError), quota limits (QuotaExceededError), or security policy violations. |
InternalError | SpiderMonkey (Firefox) | Non-standard error thrown when the JS engine encounters internal limits (e.g., “too much recursion”). |
WebAssembly.*Error | Browsers / Node.js | WebAssembly.CompileError, WebAssembly.LinkError, or WebAssembly.RuntimeError thrown during Wasm operations. |
Quick Reference Summary
Error (Base Class)
├── TypeError (Wrong value type / invalid invocation)
├── ReferenceError (Variable does not exist / TDZ)
├── SyntaxError (Malformed code or invalid JSON)
├── RangeError (Numeric parameter out of bounds)
├── URIError (Malformed URI sequence)
├── AggregateError (Collection of multiple errors)
├── SuppressedError (Resource disposal error over an existing error)
├── EvalError (Legacy eval exception)
└── Environment Errors (DOMException, WebAssembly errors, etc.)
Best Practices
1. Use Specific Error Messages
throw new Error("Password cannot be empty");
2. Avoid Empty catch Blocks
❌ Bad
catch(error) {
}
✅ Good
catch(error) {
console.log(error.message);
}
3. Use finally for Cleanup
Useful for:
- Closing database connections
- Clearing resources
- Stopping loaders
4. Do Not Overuse try…catch
Use it only around risky code.
Real-World Example
Form Validation
function login(username, password) {
try {
if(!username) {
throw new Error("Username is required");
}
if(!password) {
throw new Error("Password is required");
}
console.log("Login Successful");
} catch(error) {
console.log(error.message);
}
}
login("", "12345");