JavaScriptError Handling

Error Handling

In JavaScript an error is any value that has been thrown — either by your code with the throw statement, or by the engine when something goes wrong. The conventional value is an instance of Error (or one of its subclasses), but technically you can throw any value at all. This page covers the Error class, its built-in subtypes, and the properties you can read off any thrown object.

The Error class

Error is a constructor on the global object. Calling it produces an object with a message, a name of "Error" and a captured stack. Throwing the result starts unwinding the call stack until something catches it.

JS
const err = new Error("something went wrong");
console.log(err.name);     // "Error"
console.log(err.message);  // "something went wrong"
console.log(err.stack);    // multiline trace (host-defined format)

throw err;

The stack property is not part of the ECMAScript standard — every engine produces it, but the exact format varies. Treat it as diagnostic text, not as data you parse in production code.

Built-in error subtypes

The language defines a handful of Error subclasses for common failure modes. The engine throws specific subtypes, and you can throw them yourself when they match the situation.

  • TypeError — a value was the wrong type for the operation. The most common engine-thrown error: null.foo, calling a non-function, reassigning a const.

  • ReferenceError — referencing an identifier that has not been declared.

  • RangeError — a numeric or length value is outside the allowed range: new Array(-1), recursion that exceeds the stack.

  • SyntaxError — invalid source code. Mostly thrown at parse time; also from JSON.parse on bad input.

  • URIError — malformed encodeURI/decodeURI input.

  • EvalError — historical; rarely used in modern code.

  • AggregateError — wraps multiple errors. Thrown by Promise.any when every input rejects.

JS
try {
  null.foo;
} catch (err) {
  console.log(err.name);             // "TypeError"
  console.log(err instanceof TypeError);  // true
}
Common error properties

Every Error instance carries a few standard fields, plus some you can attach yourself:

  • name — the error class. Used by instanceof and by tools that compare names across realms.

  • message — the human-readable description passed to the constructor.

  • stack — a string trace of where the error was thrown.

  • cause — the original error this one wrapped, set via new Error("...", { cause }).

  • Anything else you assign to it (err.code, err.statusCode, err.context = {...}).

Wrapping errors with cause

When you catch a low-level error and rethrow a higher-level one, attach the original as cause so it is preserved for debugging. Browsers and Node both show the chain in their default stack output.

JS
async function loadConfig() {
  try {
    const response = await fetch("/config.json");
    return await response.json();
  } catch (original) {
    throw new Error("could not load config", { cause: original });
  }
}

loadConfig().catch((err) => {
  console.error(err);
  console.error("caused by:", err.cause);
});
Branching on error type

Use instanceof in a single catch block to branch on the kind of error. Avoid string-matching on message — translations and engine differences will break it.

JS
try {
  doSomething();
} catch (err) {
  if (err instanceof TypeError) {
    console.log("bad type:", err.message);
  } else if (err instanceof RangeError) {
    console.log("out of range:", err.message);
  } else {
    throw err;   // let unknown errors propagate
  }
}
Always re-throw what you do not understand
A blanket `catch (err) {}` is one of the most common sources of silent bugs. If the branch above does not recognise the error, rethrow it — the next handler up the stack might.
Throwing non-Error values

throw accepts any value: throw "boom", throw 42, throw { code: 500 }. The engine never inspects what you throw, but tools and humans expect an Error.

Throw Error objects, not strings
A thrown string has no stack trace and no `name`. Logging frameworks, error trackers and `instanceof` checks all assume an `Error` instance. Always `throw new Error("...")` even when you have just a message.
Errors are values

You do not have to throw an error to use it — you can build one and pass it around like any other object. This shows up in callback APIs ((err, value) => ...) and in result-style error handling.

JS
function parseAge(input) {
  const n = Number(input);
  if (!Number.isFinite(n)) return [new TypeError("not a number"), null];
  if (n < 0) return [new RangeError("negative"), null];
  return [null, n];
}

const [err, age] = parseAge("-3");
if (err) console.log(err.name, err.message);   // "RangeError" "negative"
Build an error vocabulary
Use `TypeError` for "wrong type", `RangeError` for "wrong value", and a small set of custom subclasses for domain-specific failures (`HttpError`, `ValidationError`). Consistent error types make every `catch` block easier to write.