
preserve-caught-error
Disallow losing originally caught error when re-throwing custom errors
Some problems reported by this rule are manually fixable by editor suggestions
JavaScript developers often re-throw errors in catch
blocks to add context but forget to preserve the original error, resulting in lost debugging information.
Using the cause
option when throwing new errors helps retain the original error and maintain complete error chains, which improves debuggability and traceability.
try {
await fetch("https://xyz.com/resource");
} catch(error) {
// Throw a more specific error without losing original context
throw new Error("Failed to fetch resource", {
cause: error
});
}
Rule Details
This rule enforces the use of the cause
property when throwing a new error inside a catch
block.
Checks for all built-in error types
that support passing a cause
.
Examples of incorrect code for this rule:
/* eslint preserve-caught-error: "error" */
// Not using the `cause` option
try {
// ...
} catch (error) {
}
// Throwing a new Error with unrelated cause
try {
doSomething();
} catch (err) {
const unrelated = new Error("other");
throw new Error("Something failed", { cause: });
}
// Caught error is being lost partially due to destructuring
try {
doSomething();
}
// Cause error is being shadowed by a closer scoped redeclaration.
try {
doSomething();
} catch (error) {
if (whatever) {
const error = anotherError; // This declaration is the problem.
}
}
Examples of correct code for this rule:
/* eslint preserve-caught-error: "error" */
try {
// ...
} catch (error) {
throw new Error("Something went wrong", { cause: error });
}
// When the thrown error is not directly related to the caught error.
try {
} catch (error) {
foo = {
bar() {
// This throw is not directly related to the caught error.
throw new Error("Something went wrong");
}
};
}
// No throw inside catch
try {
doSomething();
} catch (e) {
console.error(e);
}
// Ignoring the caught error at the parameter level
// This is valid by default, but this behavior can be changed
// by using the `requireCatchParameter` option discussed below.
try {
doSomething();
} catch {
throw new TypeError("Something went wrong");
}
Options
This rule takes a single option — an object with the following optional property:
requireCatchParameter
: Requires the catch blocks to always have the caught error parameter when set totrue
. By default, this isfalse
.
requireCatchParameter
Enabling this option mandates for all the catch blocks to have a caught error parameter. This makes sure that the caught error is not discarded at the parameter level.
"preserve-caught-error": ["error", {
"requireCatchParameter": true
}]
Example of incorrect code for the { "requireCatchParameter": true }
option:
/* eslint preserve-caught-error: ["error", { "requireCatchParameter": true }] */
try {
doSomething();
} catch { // Can't discard the error ❌
}
Example of correct code for the { "requireCatchParameter": true }
option:
/* eslint preserve-caught-error: ["error", { "requireCatchParameter": true }] */
try {
doSomething();
} catch(error) { // Error is being referenced ✅
// Handling and re-throw logic
}
When Not To Use It
You might not want to enable this rule if:
-
You follow a custom error-handling approach where the original error is intentionally omitted from re-thrown errors (e.g., to avoid exposing internal details or to log the original error separately).
-
You use a third-party or internal error-handling library that preserves error context using non-standard properties (e.g., verror) instead of the cause option.
-
(In rare cases) you are targeting legacy environments where the cause option in
Error
constructors is not supported.
Version
This rule was introduced in ESLint v9.35.0.
Further Reading



