Lập trình · 19/10/2026

JavaScript Error.cause: Add Context Without Losing the Original Failure

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.

Error.cause trong JavaScript: thêm ngữ cảnh mà vẫn giữ lỗi gốc

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.

Discussion

Comments 0

Sign in to comment

You need an account to join the discussion and reply to other readers.

Sign inRegister

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