VError: Rich JavaScript Error Handling
The `verror` library, currently at stable version 1.10.1, provides a set of robust error classes designed for creating richer, more debuggable JavaScript errors. Its core strength lies in implementing Joyent's best practices for error handling, offering features like `printf`-style message formatting for clear, human-readable errors, and sophisticated error chaining. `VError` allows chaining errors while preserving messages from all layers of the call stack, providing a comprehensive diagnostic trail. Conversely, `WError` wraps and chains errors but hides lower-level messages from the top-level error, making it suitable for public API endpoints where internal details should be obscured. The library also includes `SError` for stricter `printf` argument validation and `MultiError` for aggregating errors from parallel operations. While highly functional, its release cadence is slow, with the last major update being several years ago, and it primarily targets CommonJS environments, lacking native ESM support. This makes it a mature, stable choice, though potentially requiring adaptation for modern JavaScript module systems.
Common errors
-
TypeError: err.cause is not a function
cause Attempting to call `.cause()` on an object that is not an instance of `VError`, `WError`, or `SError`, or on an error that was not constructed with a `cause` argument.fixEnsure the error object is an instance of a `verror` class and was explicitly created with a `cause`. Use `VError.hasCause(err)` to safely check if an error object has a cause before attempting to access it. -
RangeError: Too few arguments for format string
cause When constructing a `VError` or `SError` with a `printf`-style message, the number of format specifiers (e.g., `%s`, `%d`) does not match the number of subsequent arguments provided for interpolation.fixReview the format string in the error constructor and ensure there is a corresponding argument for each format specifier. For example, `new VError('Hello %s')` requires one string argument after the message. -
ReferenceError: VError is not defined
cause Attempting to use `VError` with ES module `import` syntax (`import { VError } from 'verror';`) or an incorrect CommonJS `require` statement. This library is CommonJS-first.fixUse the CommonJS `require` syntax: `const VError = require('verror');` for the primary class. Other classes like `WError` are accessed as properties (e.g., `VError.WError`). The package does not natively support ESM imports.
Warnings
- gotcha CommonJS-only Module System. `verror` versions up to 1.10.1 are designed exclusively for CommonJS (`require()`). Attempting to use ES module `import` syntax will fail directly or require transpilation/bundler configuration, which might not be straightforward in all modern ESM-first environments.
- gotcha `WError` Masks Inner Messages by Default. Unlike `VError`, `WError` is designed to hide the messages of its causal errors from the primary `message` property. While useful for public APIs, this can lead to confusion if developers expect `WError` to behave like `VError` and expose the full error chain in the top-level message.
- gotcha Strict `printf`-style Formatting. `VError` and `SError` constructors use `printf`-style arguments for messages. If the number or type of format specifiers (e.g., `%s`, `%d`) does not match the provided arguments, it can lead to runtime errors or unexpected messages.
- gotcha Library Maturity and Maintenance. The `verror` package (v1.10.1) has not been actively developed for several years. While stable and functional, it may not receive updates for new Node.js features, security vulnerabilities, or modern JavaScript ecosystem best practices.
- gotcha Verbosity in Accessing Deeply Nested Causes. Retrieving a specific error from a long chain often requires multiple, repetitive calls to `err.cause().cause()...`. There's no built-in utility for direct access by depth or type within the VError API.
Install
-
npm install verror -
yarn add verror -
pnpm add verror
Imports
- VError
import { VError } from 'verror';const VError = require('verror'); - WError
import { WError } from 'verror';const VError = require('verror'); const WError = VError.WError; - MultiError
import { MultiError } from 'verror';const VError = require('verror'); const MultiError = VError.MultiError;
Quickstart
const VError = require('verror');
const fs = require('fs');
const filename = '/path/to/nonexistent_file_example'; // Ensure this file path does not exist for the example
fs.stat(filename, function (err1) {
if (err1) {
// Wrap the original filesystem error with specific context
const err2 = new VError(err1, 'Failed to retrieve stats for file "%s"', filename);
// Further wrap with a higher-level operational context
const err3 = new VError(err2, 'Critical system operation failed during file access');
console.error('Full nested error message:', err3.message);
// Expected output: Critical system operation failed during file access: Failed to retrieve stats for file "/path/to/nonexistent_file_example": ENOENT, stat '/path/to/nonexistent_file_example'
console.error('Immediate cause of err3:', err3.cause().message);
// Expected output: Immediate cause of err3: Failed to retrieve stats for file "/path/to/nonexistent_file_example": ENOENT, stat '/path/to/nonexistent_file_example'
console.error('Original underlying error:', err3.cause().cause().message);
// Expected output: Original underlying error: ENOENT, stat '/path/to/nonexistent_file_example'
// Example of adding custom informational properties
const detailedError = new VError({
cause: err3,
name: 'FileOperationError',
info: {
requestedPath: filename,
operationType: 'readStats',
timestamp: new Date().toISOString()
}
}, 'Comprehensive error for logging');
console.error('Error info properties:', detailedError.info);
} else {
console.log(`File ${filename} exists. This example requires a non-existent file.`);
}
});