An error handler can fail while trying to read message. JavaScript allows many values to be thrown, so naming a catch binding error does not establish that it is an Error object. Treat caught values as unknown input at dependency boundaries.
1. Throwing a string does not create an Error
try {
throw 'dependency failed';
} catch (value) {
console.log(typeof value); // string
console.log(value.message === undefined); // true
console.log(value); // dependency failed
}
The caught value is the original string. See MDN throw. This reproduces language behavior; prefer throwing Error instances in code you control for consistent diagnostics.
Reading message here loses the original text. A logger that records only that property may produce a failure event without useful detail. Test the data passed to logging rather than only whether catch executed.
2. Catch destructuring can fail before the handler body
try {
try {
throw null;
} catch ({ message }) {
console.log('inner handler reached');
}
} catch (error) {
console.log(error instanceof TypeError); // true
}
Object destructuring cannot operate on null, so the inner handler body never runs. The outer catch receives a new TypeError from binding initialization. See catch bindings in MDN try...catch.
A default for the message property does not solve a null input. Without a reliable contract, use a simple catch binding first and classify inside the body. Test null, undefined, numbers and custom objects explicitly.
3. Normalize at a boundary while retaining the original value
function normalizeCaught(value) {
if (value instanceof Error) return value;
return new Error('A non-Error value was thrown', { cause: value });
}
const original = new TypeError('invalid input');
console.log(normalizeCaught(original) === original); // true
const wrapped = normalizeCaught(null);
console.log(wrapped.message); // A non-Error value was thrown
console.log(wrapped.cause === null); // true
The helper retains same-realm Error identity, subtype and existing stack. Other values become causes of a new Error with a fixed message. cause can contain any value; see MDN Error.cause. These examples require runtime support for the cause option.
It neither reads an arbitrary object's message nor converts it to a string. Custom getters or conversion methods can execute code; retaining a cause is not a general serialization strategy.
instanceof Error is a simple same-realm policy. An Error from another iframe or VM may be wrapped again. Define and test that boundary separately if the application actually exchanges errors across realms.
4. Separate UI messages from diagnostics
Normalization establishes a consistent Error shape but does not choose a public message. Describe the failed operation to users and retain diagnostic causes according to the system's logging policy.
Include null, undefined, zero, false and empty strings in tests. A truthiness check cannot establish whether an error occurred because falsy values can be thrown too. Rethrowing the original value may be appropriate when the current layer adds no useful context or handling policy.
- Use a simple binding for unknown caught values.
- Read properties only under a suitable contract.
- Preserve existing Errors when context need not change.
- Retain original values when wrapping failures.
- Test falsy causes and custom object behavior.
All six bilingual blocks ran with Node.js, alongside identity, falsy-cause and throwing-message-getter checks. Live logging and iframe integration were not tested.




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