The throw statement in JavaScript is used to generate custom errors. By using throw, you can create exceptions and handle them using the try…catch statement, allowing you to manage error handling in a more controlled and informative manner.
Basic Syntax
The basic syntax of the throw statement is as follows:
throw expression;
Do not place a line break between the
throwkeyword and the expression. JavaScript’s automatic semicolon insertion (ASI) will treat the line break as the end of the statement, resulting inthrow;which is a syntax error.
The expression can be any valid JavaScript expression, but you should always throw an Error object (or a subclass of it). Throwing strings or numbers is technically possible but prevents access to stack traces and makes debugging harder.
Always use throw new Error('message') or a custom Error subclass. Throwing a plain string like throw 'something went wrong' produces no stack trace and cannot be reliably caught by type.
Example of throw Statement
Here is an example demonstrating the use of the throw statement:
function divide(a, b) {
if (b === 0) {
throw new Error('Division by zero is not allowed.');
}
return a / b;
}
try {
console.log(divide(4, 2)); // Output: 2
console.log(divide(4, 0)); // Throws error
} catch (error) {
console.error(error.message); // Output: Division by zero is not allowed.
}
In this example, if the divisor b is zero, an error is thrown with a custom message. The try…catch block is used to handle the error and display the message.
Using throw with Custom Errors
You can create custom error objects by extending the built-in Error class. This allows you to define specific error types for your application:
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
function validateAge(age) {
if (age < 0 || age > 120) {
throw new ValidationError('Invalid age value.');
}
return true;
}
try {
validateAge(150); // Throws ValidationError
} catch (error) {
if (error instanceof ValidationError) {
console.error(error.message); // Output: Invalid age value.
} else {
console.error('Unknown error:', error);
}
}
Re-throwing Errors
In some cases, you might want to handle an error partially and then re-throw it to be handled by another try...catch block:
function parseJSON(jsonString) {
try {
return JSON.parse(jsonString);
} catch (error) {
if (error instanceof SyntaxError) {
console.error('JSON parsing error:', error.message);
throw error; // Re-throw the error
} else {
throw new Error('Unknown error');
}
}
}
try {
parseJSON('invalid JSON');
} catch (error) {
console.error('Caught an error:', error.message);
}
Error Chaining with cause
ES2022 introduced the cause property, which lets you attach the original error when throwing a new one. This preserves the full error chain without losing context:
async function fetchUser(id) {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
throw new Error('Failed to fetch user', { cause: error });
}
}
try {
await fetchUser(42);
} catch (error) {
console.error(error.message); // "Failed to fetch user"
console.error(error.cause.message); // "HTTP 404" (the original error)
}
The cause property is supported in all modern browsers and Node.js 16.9+.
Best Practices
Here are best practices for using the throw statement effectively:
- Always throw Error objects: Never throw strings or numbers. Error objects provide a
.message,.name,.stack, and optionally.cause– all essential for debugging. - Use descriptive messages: Write error messages that explain what went wrong and, where possible, include the invalid value or context.
- Create custom error types: Define custom Error subclasses for domain-specific scenarios. This lets calling code use
instanceofto handle different errors differently. - Use try…catch: Always handle thrown errors using
try...catchblocks to prevent your application from crashing. - Re-throw with context: When catching and re-throwing, use the
causeoption to preserve the original error. - Don’t catch too broadly: Avoid catching
Errorat the top level and silently ignoring it. Log the error or handle it meaningfully.
FAQs
Common questions about the JavaScript throw statement:
throw 'error' produces no stack trace, no .name property, and no .cause support. Always throw new Error('message') or a custom Error subclass.return exits a function and passes a value to the caller. throw exits a function and jumps to the nearest catch block in the call stack. If no catch exists, the program crashes. Use throw for exceptional conditions that the calling code must handle, not for normal control flow.async function, throw rejects the returned Promise. The calling code can catch it with try...catch (inside another async function) or with .catch() on the Promise. The behavior is equivalent to calling Promise.reject(new Error('message')).ValidationError might trigger a user-facing message, while an HttpError might trigger a retry. If a generic Error with a descriptive message is enough, there is no need to create a custom class.cause property (ES2022) lets you chain errors by attaching the original error to a new one: throw new Error('High-level message', { cause: originalError }). This preserves the full error context, making debugging easier when errors are caught and re-thrown across multiple layers.Summary
The throw statement lets you generate custom errors that propagate up the call stack until a catch block handles them. Always throw Error objects or custom subclasses – never plain strings or numbers.
Extend the Error class to create domain-specific error types that calling code can identify with instanceof. Use the cause property (ES2022) to preserve the original error when re-throwing.
Watch out for automatic semicolon insertion – never place a line break between throw and the expression.

