JavaScript Exception Handling

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 TypeTrigger CauseExample
ReferenceErrorAccessing an undeclared variableconsole.log(x);
TypeErrorPerforming an invalid operation on a value"text".toUpperCase() works, but (123).toUpperCase() fails
SyntaxErrorInvalid JavaScript syntax (usually caught at parse time)JSON.parse("{ bad json }")
RangeErrorA numeric variable or parameter is outside its valid rangenew 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 TypeEnvironmentCause
DOMExceptionWeb APIs / Node.jsRaised by Web APIs for operations like aborted fetch requests (AbortError), quota limits (QuotaExceededError), or security policy violations.
InternalErrorSpiderMonkey (Firefox)Non-standard error thrown when the JS engine encounters internal limits (e.g., “too much recursion”).
WebAssembly.*ErrorBrowsers / Node.jsWebAssembly.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");

Leave a Comment