JavaScript Error Handling

JavaScript error handling lets your program respond to problems instead of crashing. When something goes wrong, JavaScript throws an error object; you catch it with try...catch and decide what to do.

try...catch

Code in try runs normally. If any line throws, execution jumps to catch, which receives the error.

try {
  const data = JSON.parse("{ bad json }");
  console.log(data);
} catch (err) {
  console.log("Could not parse:", err.name);
}
console.log("The program keeps running");
Could not parse: SyntaxError
The program keeps running

finally

finally runs whether or not an error happened. It is the place for cleanup, such as hiding a loading spinner.

function divide(a, b) {
  try {
    if (b === 0) throw new Error("Cannot divide by zero");
    return a / b;
  } catch (err) {
    console.log("Error:", err.message);
    return null;
  } finally {
    console.log("divide() finished");
  }
}
console.log(divide(10, 2));
console.log(divide(1, 0));
divide() finished
5
Error: Cannot divide by zero
divide() finished
null

Throwing Custom Errors

Throw your own errors with throw new Error(message). For different kinds of failures, extend Error into custom classes and check them with instanceof. The cause option links an error to the one that caused it.

class ValidationError extends Error {
  constructor(field, message) {
    super(message);
    this.name = "ValidationError";
    this.field = field;
  }
}

function register(age) {
  if (age < 13) throw new ValidationError("age", "Must be at least 13");
  return "Registered";
}

try {
  register(10);
} catch (err) {
  if (err instanceof ValidationError) console.log(err.field, "->", err.message);
  else throw err;
}
age -> Must be at least 13

Built-in Error Types

  • ReferenceError: using a variable that does not exist
  • TypeError: wrong type, e.g. calling undefined as a function
  • SyntaxError: invalid code or invalid JSON
  • RangeError: number out of allowed range

Errors in Async Code

With async/await, wrap awaited calls in try...catch. With promise chains, use .catch(). Unhandled promise rejections show up in the console.

async function load() {
  try {
    await Promise.reject(new Error("Network down"));
  } catch (err) {
    console.log("Handled:", err.message);
  }
}
load();
Handled: Network down

Frequently asked questions

Should I wrap all my code in try catch?

No. Catch errors where you can actually handle them, like showing a message or retrying. Catching everything hides bugs.

Can I throw a string instead of an Error?

You can, but you lose the stack trace and the name/message properties. Always throw Error objects.

Does try catch work with setTimeout?

Not around it. The callback runs later, outside the try. Put the try...catch inside the callback, or use promises with await.