If you are a JavaScript developer, you might have ran into error messages like:
- Failed to extract text from the file
- Failed to save entity
- And so on...
But what exactly led to failing of an entire high-level operation?
We don't know the answer because we didn't do a good enough job to preserve the error context, when throwing a specific error that is a symptom of a more low level cause.
For years, JavaScript developers have struggled to preserve appropriate error context when throwing exceptions, as there was no in-built mechanism provided by the language to attach error cause, unlike other languages like Java, Python and C#.
This problem was solved by Node, when they finally added a cause property to their Error constructor in v16.9.0
, which means you could now throw exceptions like so:
try {
await fetch("some-resource");
}
catch(error) {
throw new Error("Specific error message", {
cause: error
});
}
This means you now get to preserve the entire context of the original error that led to throwing of a high level exception (including its message and stack trace).
Table of Contents
1. The problem
2. The solution?
3. Installation
4. Configuration
4.1. VSCode Extension
5. Conclusion
The problem
This feature has been available in Node for a while now (added in 2022), and although it solves the big problem of not having a standard way to preserve error context, it introduces a new problem as its completely optional to attach the error cause to new exceptions.
The solution?
What we need is a static analysis tool to enforce this as a coding convention — ensuring developers always attach available causes when throwing new errors.
While ESLint is the go-to tool for code quality in JavaScript, it doesn’t provide any built-in rule for this.
So I decided to write one myself!
Introducing eslint-plugin-error-cause — an ESLint plugin that flags any throw new Error(...)
calls that omit the cause when inside a catch block.
Let's talk about how we can set this up in our projects.
Installation
The very first thing you need to do is install the plugin and eslint
as dev dependencies in your project. This example uses pnpm, but you could use a package manager of your choice.
pnpm add -D eslint eslint-plugin-error-cause
Configuration
1. Once installed, create an eslint.config.js
file at the root of your project.
2. There are two ways to add this plugin to your eslint configuration.
- Use the in-built
recommended
config, that enablesno-swallowed-error-cause
rule with a severity level ofwarn
.
import errorCause from "eslint-plugin-error-cause";
import { defineConfig } from "eslint/config";
export default defineConfig([errorCause.configs.recommended]);
- Enable the rule manually with a severity level of your choice.
import errorCause from "eslint-plugin-error-cause";
import { defineConfig } from "eslint/config";
export default defineConfig([
{
plugins: {
"error-cause": errorCause,
},
rules: {
"error-cause/no-swallowed-error-cause": "warn",
},
},
]);
3. Setup lint scripts in your package.json
.
"scripts": {
...
"lint": "eslint .",
"lint:fix": "pnpm lint --fix",
...
},
4. Use the lint
script to report all the lint errors in your code. 🎊
> pnpm lint
3:10 error 'rootError' is defined but never used no-unused-vars
4:5 warning Include the original caught error as the `cause` of the custom error error-cause/no-swallowed-error-cause
5. Use --fix
to fix any instances on no-swallowed-error-cause
.
> pnpm lint:fix
VSCode Extension
Almost everyone uses ESLint's VSCode extension to detect and fix lint errors in-place when coding.
If you don't have it setup, follow these steps:
1. Install the ESLint extension.
2. Add this setting to your workspace/user settings.json
. You could add/omit the JavaScript flavours based on your project needs.
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
3. Now you can detect and fix swallowed error causes using VSCode's UI.
Conclusion
So that's all about how you could setup my new plugin in your projects, and never again worry about missing important error context when throwing custom exceptions!
This plugin is still very new and might not cover some edge-cases. I welcome any interested readers to visit the GitHub repo, open issues, send in pull requests, or even give it a star and share with your connections if this helped you in any way 😁
Github: https://github.com/Amnish04/eslint-plugin-error-cause
NPM: https://www.npmjs.com/package/eslint-plugin-error-cause
Top comments (6)
yeah this is super clutch tbh, i’ve lost so much time digging for those swallowed errors - you think more teams would actually adopt this kinda stuff if it flagged in ci?
I'd say every team should properly enforce this standard. Our team at DeepStructure found it useful to the point I decided to write this plugin!
Love this, autofix support and VSCode integration are super helpful touches.
Any plans for handling custom error types or async callbacks in the future?
Glad you liked it! Happy to handle any remaining edge cases/improvements as follow ups.
Please file issues on the Github repo with full details and examples, so we can keep track of it. You could even send in a PR if you want to :)
github.com/Amnish04/eslint-plugin-...
pretty cool, i've always wanted a better way to catch where stuff blows up in my code
Glad you found it useful! I would really appreciate a star to the repo as it helps promote the project :D