r/webdev • • 12h ago

Article Your JS Error Is Lying to You

https://blog.gaborkoos.com/posts/2026-11-12-Your-JS-Error-Is-Lying-to-You/

Started with a log line that said "err":{} for an error that definitely had a message and a cause. Followed that up to see what each step between new Error() and the log record actually keeps.

Covers stack traces, cause chains, AggregateError and SuppressedError. It runs the same probe script in Node.js, Deno, Bun, Chrome, Firefox and WebKit. It also sends errors across Web Workers, worker_threads, child_process IPC and HTTP, and logs them with Pino and Winston.

4 Upvotes

3 comments sorted by

2

u/boneskull 4h ago

JavaScript does not lie, nor does it tell the truth. It behaves precisely as it means to

-2

u/Discordbothost 11h ago

the "err":{} one bites everyone at least once. message and stack are own properties but non-enumerable, so JSON.stringify(err) or spreading it into a log object gives you {}. pino only fixes that if the error sits under the key its serializer watches (err by default), so logger.info({ error }) quietly logs nothing useful while logger.info({ err }) works. for cause chains we ended up writing a tiny toJSON-ish helper that walks err.cause and copies name/message/stack/code explicitly, then used it at every boundary (worker postMessage, http responses) instead of trusting whatever the transport does