An error message says what broke. The stack trace says how you got there, and that is nearly always the piece missing before a fix is possible.
Provided you read it the right way round, and know what it leaves out.
Definition
The stack trace is the text carried by the stack property of an Error object. It lists the functions passed through, most recent first, with file, line and column.
function level3() { throw new Error("nothing works"); }
function level2() { level3(); }
function level1() { level2(); }
try {
level1();
} catch (err) {
console.log(err.stack);
}
// Error: nothing works
// at level3 (index.js:1:27)
// at level2 (index.js:2:21)
// at level1 (index.js:3:21)The first line repeats the name and the message, the following ones read top to bottom like a walk backward. The top line is where the break happened, the lower ones tell the sequence that led to it.
What it actually tells you
- The trace is captured at creation. It describes where the
new Errorhappened, not the throw nor the catch. Creating the error far from the incident destroys the information. - It stops at a fixed depth. Under Node.js and in browsers built on the same engine,
Error.stackTraceLimitis ten by default and can be raised during development. - Its format is not standardized. It varies between engines: useful to read and to log, never to be parsed as structured data.
What asynchronous code erases
An error thrown inside a timer or request callback leaves from an almost empty stack: the calling code handed control back long ago and no longer appears.
function throwLater() {
setTimeout(() => { throw new Error("inside the timer"); }, 0);
}
throwLater();
// The trace does not mention throwLater: it already left the stackTwo reflexes limit the damage. Prefer await over callbacks, since recent engines rebuild the trace across the waits. And enrich the error where you catch it, rethrowing with the cause property, which keeps the original error and its own trace.
On minified code, the trace points at one enormous line and one-letter names. The mapping file produced at build time, once handed to the error tracking tool, translates the trace back to the original files. Without it, a production trace is unreadable.
Frequently asked questions
How do you strip internal lines from a trace?
With Error.captureStackTrace(this, MyClass) inside the constructor of a custom error: the trace then starts at the caller, without the factory's own lines. The function belongs to the V8 engine, so to Node.js and Chrome, and should be guarded by a presence test in shared code.
Should the trace be shown to the user?
No. It exposes file paths and internal details of no value whatsoever to the person using the product. The screen gets an understandable message, the logs get the full trace.
Why does my trace hold only one line?
Often because the error was built somewhere other than where the incident happened, for instance from a string rethrown inside a catch. Rethrow the original object, or create the new error with the old one as its cause so both traces survive.