The log says “Could not load configuration”, but a newly thrown Error discarded the original failure. Context identifies the operation; the cause preserves evidence from the failing point. Error.cause links those layers.
The examples use local data and send no logs externally. Check support in the Node.js or browser runtime actually deployed.
1. Retain the original error reference
const original = new SyntaxError('Invalid configuration text');
const contextual = new Error('Could not load configuration', {
cause: original
});
console.log(contextual.message); // Could not load configuration
console.log(contextual.cause === original); // true
console.log(contextual.cause.name); // SyntaxError
The wrapper adds operation context while preserving the original object. There is no need to concatenate its stack into the message just to establish the relationship.
Rethrow the original when the current layer adds no useful context. Repeated generic wrappers can lengthen a chain without improving diagnosis.
2. Wrap at a meaningful operation boundary
function readConfigText(text) {
if (typeof text !== 'string') {
throw new TypeError('Expected configuration text');
}
try {
return JSON.parse(text);
} catch (cause) {
throw new Error('Configuration JSON could not be parsed', { cause });
}
}
try {
readConfigText('{');
} catch (error) {
console.log(error.message); // Configuration JSON could not be parsed
console.log(error.cause instanceof SyntaxError); // true
}
The function parses syntax, not a configuration schema. Valid JSON null and arrays still pass; required fields, types and ranges need separate validation.
Do not automatically embed configuration contents in messages. Use appropriate operation identifiers or correlation IDs rather than retaining complete inputs by default.
3. A cause need not be an Error
const failure = new Error('Import failed', {
cause: { code: 'INVALID_ROW', row: 4 }
});
console.log(failure.cause instanceof Error); // false
console.log(failure.cause.code); // INVALID_ROW
console.log(failure.cause.row); // 4
A cause can intentionally hold structured data. It can also be a string, null or another thrown value. Do not assume error.cause.message exists in every catch handler.
Publish stable error codes through an explicit contract rather than parsing human-readable messages. Validate unknown values before reading fields.
4. JSON.stringify does not export all error details
const original = new Error('Parse failed');
const error = new Error('Load failed', { cause: original });
console.log(JSON.stringify(error)); // {}
const record = {
name: error.name,
message: error.message,
causeName: error.cause instanceof Error ? error.cause.name : null
};
console.log(JSON.stringify(record));
// {"name":"Error","message":"Load failed","causeName":"Error"}
This ordinary Error has no extra enumerable fields, so serialization produces an empty object. Constructor-created cause is non-enumerable. Calling stringify alone does not establish that diagnostic information was preserved.
The selected record is an illustration, not a general serializer or public response format. Check actual logger support, depth limits and cycle handling for customized error data. Messages, stacks and causes need filtering before leaving trusted environments.
5. Separate user messages from diagnostics
UI messages should help users decide what to do next. Internal chains may contain filenames and library details; do not return entire Error objects simply because a cause exists.
A cause models an underlying failure with added context. Multiple independent failures need a suitable multi-error representation rather than an invented causal chain.
6. Verify context and cause
- Retain the original object, not just its message.
- Describe the actual failed operation.
- Return valid JSON and wrap syntax failures with SyntaxError causes.
- Reject wrong input types before parsing.
- Verify actual logger output.
- Handle missing and non-Error causes intentionally.
The snippets ran locally with Node.js, covering identity, parsing and JSON output. No application logger or UI integration was tested. References: Error.cause and Error.
Add context at useful boundaries while retaining the original cause. The benefit is connected evidence, not more wrapper layers.




No comments yet. Be the first to share your thoughts.