JavaScript Error Handling

Error handling allows programs to catch and handle errors gracefully instead of crashing. The try...catch statement provides structured error handling that prevents scripts from stopping when errors occur, enabling robust, user-friendly applications.

Try...Catch

The try block contains code that might cause errors—risky operations like parsing JSON, accessing undefined properties, or calling functions that might throw. JavaScript attempts to execute the try block normally. If no error occurs, the catch block is skipped. try...catch provides controlled error handling.

If an error occurs in the try block, execution immediately stops and jumps to the catch block. The catch block receives an error object containing information about what went wrong. This prevents the error from stopping your entire program—instead, you handle it gracefully with fallback logic, user messages, or logging.

The optional finally block always executes, whether an error occurred or not. finally runs after try and catch complete, making it perfect for cleanup operations like closing files, clearing timers, or resetting state. Even if catch returns or throws, finally still executes. This guarantees cleanup happens.

The error object passed to catch contains name and message properties. error.name describes the error type ("TypeError", "ReferenceError", etc.), while error.message provides a human-readable description. You can also access error.stack for the call stack trace, useful for debugging.

You can create and throw custom errors with the throw statement. While try...catch catches errors, throw generates them. throw new Error("Custom message") creates an error you can catch elsewhere. Custom errors make code more expressive by signaling specific problem conditions with meaningful messages.

// Basic try...catch
try {
  const result = riskyFunction(); // May throw error
  console.log(result);
} catch (error) {
  console.log("Error:", error.message);
}

// With finally
try {
  console.log("Trying...");
  throw new Error("Something went wrong");
} catch (error) {
  console.log("Caught:", error.message);
} finally {
  console.log("Finally always runs");
}

// Real example
try {
  JSON.parse("invalid json");
} catch (error) {
  console.log("JSON parse error:", error.message);
}

Throwing Errors

The throw statement creates and throws custom errors, immediately stopping execution and transferring control to the nearest catch block. throw lets you signal error conditions in your code when something goes wrong. Any code after throw doesn't execute—control jumps directly to catch.

Technically you can throw any value—strings, numbers, booleans, or objects. throw "Error message" works. However, this is poor practice because different types of throws are hard to handle consistently. Always throw Error objects for proper error handling.

Using Error objects (throw new Error("message")) is strongly recommended because they include stack traces and follow JavaScript's error handling conventions. Error objects have name, message, and stack properties that provide debugging information. They're standardized and work consistently with try...catch.

JavaScript has different built-in error types for specific situations: Error (generic), TypeError (wrong type), ReferenceError (undefined variable), RangeError (number out of range), SyntaxError (code syntax issues). You can throw these specific types to be more descriptive: throw new TypeError("Expected string").

Custom errors are essential for input validation and domain-specific error handling. When validating user input, throw descriptive errors: if (!email.includes("@")) throw new Error("Invalid email"). This makes error conditions explicit, improves debugging, and allows calling code to handle problems appropriately.

// Throw string
function checkAge(age) {
  if (age < 18) {
    throw "Too young!";
  }
  return true;
}

// Throw Error object
function divide(a, b) {
  if (b === 0) {
    throw new Error("Cannot divide by zero");
  }
  return a / b;
}

try {
  divide(10, 0);
} catch (error) {
  console.log(error.message);
}

// Custom error types
function validateEmail(email) {
  if (!email.includes("@")) {
    throw new TypeError("Invalid email format");
  }
  return true;
}