preserve-caught-error
重新抛出自定义错误时,禁止丢失最初捕获的错误
在 配置文件 中使用来自 @eslint/js 的 recommended 配置可以启用此规则
此规则报告的一些问题可通过编辑器 建议 手动修复
JavaScript 开发者经常在 catch 块中重新抛出错误以添加上下文,但忘记保留原始错误,导致调试信息丢失。
🌐 JavaScript developers often re-throw errors in catch blocks to add context but forget to preserve the original error, resulting in lost debugging information.
在抛出新错误时使用 cause 选项有助于保留原始错误并保持完整的错误链,从而提高可调试性和可追踪性。
🌐 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
此规则强制在 catch 块中抛出新错误时使用 cause 属性。
🌐 This rule enforces the use of the cause property when throwing a new error inside a catch block.
检查所有支持传递 cause 的内置 error types。
🌐 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) {
throw new Error("Something went wrong: " + error.message);
}
// Throwing a new Error with unrelated cause
try {
doSomething();
} catch (err) {
const unrelated = new Error("other");
throw new Error("Something failed", { cause: unrelated });
}
// Caught error is being lost partially due to destructuring
try {
doSomething();
} catch ({ message, ...rest }) {
throw new Error(message);
}
// Cause error is being shadowed by a closer scoped redeclaration.
try {
doSomething();
} catch (error) {
if (whatever) {
const error = anotherError; // This declaration is the problem.
throw new Error("Something went wrong", { cause: error });
}
}
符合此规则的正确代码示例:
🌐 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 properties:
requireCatchParameter:当设置为true时,要求 catch 块始终包含被捕获的错误参数。默认情况下为false。errorClassNames:要检查的其他错误类名称以保留原因。默认情况下,这是[]。
requireCatchParameter
启用此选项要求所有的 catch 块都必须有一个捕获的错误参数。这确保了捕获的错误不会在参数级别被丢弃。
🌐 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
}]
针对 { "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 ❌
throw new Error("Something went wrong");
}
{ "requireCatchParameter": true } 选项的正确代码示例:
🌐 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
}
errorClassNames
默认情况下,此规则仅检查内置的 Error 类型(Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError、AggregateError)。使用 errorClassNames 也可以检查自定义错误类。
🌐 By default, this rule checks only the built-in Error types (Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError, AggregateError). Use errorClassNames to also check custom error classes.
每个条目可以是字符串或对象:
🌐 Each entry can be either a string or an object:
- 字符串指定类名。构造函数假定接受选项对象作为第二个参数,与内置的
Error签名匹配。 - 当构造函数在不同位置接受选项对象时,会使用带有
name和argumentPosition的 对象。argumentPosition是从1开始计数的。
{
"rules": {
"preserve-caught-error": ["error", {
"errorClassNames": [
"AppError",
{ "name": "APIError", "argumentPosition": 3 }
]
}]
}
}
针对 { "errorClassNames": ["AppError"] } 选项的错误代码示例:
🌐 Example of incorrect code for the { "errorClassNames": ["AppError"] } option:
/* eslint preserve-caught-error: ["error", { "errorClassNames": ["AppError"] }] */
class AppError extends Error {}
try {
doSomething();
} catch (err) {
throw new AppError("Something failed");
}
{ "errorClassNames": ["AppError"] } 选项的正确代码示例:
🌐 Example of correct code for the { "errorClassNames": ["AppError"] } option:
/* eslint preserve-caught-error: ["error", { "errorClassNames": ["AppError"] }] */
class AppError extends Error {}
try {
doSomething();
} catch (err) {
throw new AppError("Something failed", { cause: err });
}
针对 { "errorClassNames": [{ "name": "APIError", "argumentPosition": 3 }] } 选项的错误代码示例:
🌐 Example of incorrect code for the { "errorClassNames": [{ "name": "APIError", "argumentPosition": 3 }] } option:
/* eslint preserve-caught-error: ["error", { "errorClassNames": [{ "name": "APIError", "argumentPosition": 3 }] }] */
class APIError extends Error {
constructor(message, statusCode, options) {
super(message, options);
this.statusCode = statusCode;
}
}
try {
doSomething();
} catch (err) {
throw new APIError("Request failed", 500);
}
{ "errorClassNames": [{ "name": "APIError", "argumentPosition": 3 }] } 选项的正确代码示例:
🌐 Example of correct code for the { "errorClassNames": [{ "name": "APIError", "argumentPosition": 3 }] } option:
/* eslint preserve-caught-error: ["error", { "errorClassNames": [{ "name": "APIError", "argumentPosition": 3 }] }] */
class APIError extends Error {
constructor(message, statusCode, options) {
super(message, options);
this.statusCode = statusCode;
}
}
try {
doSomething();
} catch (err) {
throw new APIError("Request failed", 500, { cause: err });
}
已知限制
🌐 Known Limitations
此规则严格通过 AST 中的名称来识别自定义错误类。由于它不跟踪范围或类型信息,局部遮蔽会导致误报。
🌐 This rule identifies custom error classes strictly by their name in the AST. Because it does not trace scope or type information, local shadowing will cause false positives.
如果配置的错误类被本地声明所覆盖,该规则仍会标记它,即使本地类具有不同的签名并且不接受 cause 选项。
🌐 If a configured error class is shadowed by a local declaration, the rule will still flag it, even if the local class has a different signature and does not accept a cause option.
误报示例:
🌐 Example of a false positive:
/* eslint preserve-caught-error: ["error", { errorClassNames: ["AppError"] }] */
function makeWrapped() {
// Local class shadows the intended global/imported "AppError"
class AppError {
constructor(err) {
this.original = err;
}
}
try {
doSomething();
} catch (err) {
// Falsely reported as "missingCause" despite the local signature not accepting options
throw new AppError(err);
}
}
何时不使用
🌐 When Not To Use It
如果你符合以下情况,则可能不想启用此规则:
🌐 You might not want to enable this rule if:
- 你遵循自定义错误处理方法,其中故意从重新抛出的错误中省略原始错误(例如,为了避免暴露内部详细信息或单独记录原始错误)。
- 你使用第三方或内部的错误处理库,这些库通过非标准属性(例如 verror)保留错误上下文,而不是使用 cause 选项。
- (在罕见情况下)你正在针对不支持
Error构造函数中 cause 选项的旧环境。
版本
此规则是在 ESLint v9.35.0 中引入。