preserve-caught-error

Require the caught error as the cause of a new error thrown in catch

warn by defaulthas suggestions

When a catch block throws a new error, the one it caught is gone, along with its message and stack trace. The log says "Could not load the config" but not why. Pass the caught error as cause to keep both.

The rule checks throw new Error() in a catch block, and the other built-in classes such as TypeError and AggregateError. A throw in a function inside the block isn't checked.

The report comes with a suggestion that adds { cause: error }.

This rule is on by default.

Configure#

On by default as a warning. To turn it off:

bunfig.toml
[lint.rules]
"preserve-caught-error" = "off" # or "error"

Every option, set to its default:

bunfig.toml
[lint.rules]
"preserve-caught-error" = ["warn", {
  requireCatchParameter = false,
  errorClassNames = [], # list of strings or objects
}]

Examples#

example.js1 problem
try {
  loadConfig();
} catch (error) {
  throw new Error("Could not load the config");
}

The cause must be the caught error itself, not a part of it:

example.js1 problem
try {
  loadConfig();
} catch (error) {
  throw new Error("Could not load the config", { cause: error.message });
}

Destructuring the parameter loses the rest of the error:

example.js1 problem
try {
  loadConfig();
} catch ({ message }) {
  throw new Error(`Could not load the config: ${message}`);
}
example.jsNo problems
try {
  loadConfig();
} catch (error) {
  throw new Error("Could not load the config", { cause: error });
}

try {
  saveConfig();
} catch (error) {
  report(error);
  throw error;
}

Options#

OptionDefaultWhat it does
requireCatchParameterfalseAlso report a new error thrown in a catch that has no parameter.
errorClassNames[]Your own error classes to check as well.
example.jswith { "requireCatchParameter": true }1 problem
try {
  loadConfig();
} catch {
  throw new Error("Could not load the config");
}

Each errorClassNames entry is the name of a class that takes { cause } as its second argument, like Error:

example.jswith { "errorClassNames": ["ConfigError"] }1 problem
try {
  loadConfig();
} catch (error) {
  throw new ConfigError("Could not load the config");
}

If { cause } is a different argument, make the entry an object that says which one, counting from 1:

example.jswith { "errorClassNames": [{ "name": "HttpError", "argumentPosition": 3 }] }1 problem
try {
  await fetchUser(id);
} catch (error) {
  throw new HttpError(502, "Could not fetch the user");
}
example.jswith { "errorClassNames": [{ "name": "HttpError", "argumentPosition": 3 }] }No problems
try {
  await fetchUser(id);
} catch (error) {
  throw new HttpError(502, "Could not fetch the user", { cause: error });
}