throw in JavaScript: raising an error on purpose

throw raises an error on purpose and stops the current flow until the first catch block: it is how a function refuses invalid data outright.
3 min read
Believemy logo

A function handed data it cannot process has two options: return a shaky value and let the problem spread, or refuse outright. throw is the keyword of the outright refusal.

Used well, it turns a distant, baffling failure into a precise message raised at the exact spot where the bad data entered the program.


Definition

throw raises an error. The current function stops immediately, and the engine walks back up the call stack looking for the first available catch. If it finds none, the error is printed to the console and the process stops.

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

try {
  divide(10, 0);
} catch (error) {
  console.log(error.name, ":", error.message);
  // Error : Division by zero
}

Note the new in front of Error: you do not raise a message, you raise an object built for the occasion.


Raise an Error object, never a string

The language accepts any value at all: a number, a string, some random object. An expensive freedom, and the table below explains why.

What is raisedWhat the catching block gets
A stringNo call stack, no type, a bare message
A plain objectUseful properties, but still no stack
An instance of ErrorType, message, call stack and possible cause

The call stack is the irreplaceable part: it names the failing line and the path taken to reach it.


Your own error types

As soon as an application separates several families of problems, creating your own types by inheriting from Error becomes worthwhile. Catching code can then sort with instanceof instead of comparing messages, which change on the first rewrite.

JAVASCRIPT
class PaymentError extends Error {
  constructor(message, code) {
    super(message);
    this.name = "PaymentError";
    this.code = code;
  }
}

try {
  throw new PaymentError("Card declined", "card_declined");
} catch (error) {
  console.log(error instanceof Error);    // true
  console.log(error.name, error.code);    // PaymentError card_declined
}

The construction rests on class, extends and super, three keywords that are enough to obtain a complete error type. The call to super(message) is mandatory: it is what fills in the message and captures the stack.

Good to know

To re-raise an error with added context without losing the original, pass it as a cause: throw new Error("Unreadable configuration", { cause: error }). The block above finds the first error again in error.cause.


Frequently asked questions

Question

Where should you raise, and where should you catch?

Raise as close as possible to the offending data, where the program still has the context to say what is wrong. Catch as close as possible to the user, where a decision can be made: show a message, retry, fall back to a default.


Question

Does a raised error stop the entire program?

No, it stops the current flow up to the first block able to receive it. In a browser, an uncaught error interrupts the event handler concerned but the page stays alive. In Node.js it ends the process, which is why servers wrap every request in a catching block.


Question

Is it better to raise an error or to return null?

Returning null fits when absence is a normal result, such as a search that finds nothing. Raise an error when the situation is abnormal and no reasonable caller can continue, such as a missing API key. The criterion is not severity but this question: does the caller have anything sensible to do with that value.

Related terms

Discover our javaScript glossary

Every word of JavaScript explained simply: keywords, built-in objects, methods, errors and concepts. Clear definitions and examples that actually run, to learn and to troubleshoot.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.