Skip to main content

What a stack trace contains

A stack trace is the string in the error.stack property that holds the error type, its message and the chain of function calls that led to the failure. Each at ... line shows one function, its file, line and column.

Theory

TL;DR

  • The call stack is the structure where the engine keeps the list of functions currently running.
  • When an error is created, the engine takes a snapshot of that stack and stores it in error.stack.
  • The first line of stack is Name: message, followed by at function (file:line:column) lines.
  • Reading order: the failure site on top, its callers below.
  • Asynchronous calls (setTimeout, promises, await) can break the stack, because they run in a later iteration of the event loop.

Quick example

javascript
function a() { b(); } function b() { c(); } function c() { throw new Error("Something went wrong!"); } try { a(); } catch (err) { console.log(err.stack); }

Console output:

text
Error: Something went wrong! at c (<anonymous>:8:9) at b (<anonymous>:4:3) at a (<anonymous>:1:3) at <anonymous>:12:3

What the call stack is

The call stack is a data structure in which JavaScript keeps track of which functions are currently running and in what order they were called.

When an error occurs, the engine takes a snapshot of that stack, the stack trace, which shows where exactly and in what sequence the code that led to the error was called.

How to read stack trace lines

Every line in stack is one call level. Taking the example above apart:

text
Error: Something went wrong! <- error type and message at c (<anonymous>:8:9) <- function c() threw the error at b (<anonymous>:4:3) <- c() was called from b() at a (<anonymous>:1:3) <- b() was called from a() at <anonymous>:12:3 <- a() was called in the global scope

Each line gives you:

  • the function name (at c, at b, at a);
  • the file (or <anonymous> when the code has no file name);
  • the line and column number where the call happened.

Where the stack lives: the .stack property

When an error object is created (new Error()), it automatically gets a .stack property that contains:

  • the error name (Error, TypeError and so on);
  • the message (message);
  • the stack trace itself.
javascript
const err = new Error("Network failure"); console.log(err.stack);

Output:

text
Error: Network failure at <anonymous>:1:13

In real error handling it is convenient to log both parts separately:

javascript
try { throw new Error("Unexpected failure"); } catch (e) { console.error("Error:", e.message); console.error("Call stack:\n", e.stack); }

Custom errors have a stack too. If you build your own class with class ... extends Error, the .stack property is available exactly the same way:

javascript
class ValidationError extends Error { constructor(message) { super(message); this.name = "ValidationError"; } } try { throw new ValidationError("Invalid email"); } catch (e) { console.log(e.stack); }

Output:

text
ValidationError: Invalid email at <anonymous>:8:9 at ...

The stack for asynchronous errors

Asynchronous calls (through setTimeout, Promise, await) can break the stack, because they run later, in a different iteration of the event loop.

javascript
function a() { setTimeout(() => { throw new Error("Timer failure"); }, 0); } a();

Output:

text
Uncaught Error: Timer failure at Timeout._onTimeout (<anonymous>:3:11)

Only part of the stack is visible here, because the error happened in a different execution context. There is no a() in the trace: by the time the timer fired, its frame had already left the stack.

Practical use

A stack trace lets you:

  • find the failure site in the code quickly;
  • understand which functions led to the error;
  • store the trace in logs and monitoring systems (Sentry, GlitchTip, Logtail);
  • build your own backend for analysing user-facing errors.

The parts of an error in one place:

ElementWhat it means
Error.messageThe error text
Error.nameThe error type (TypeError, ReferenceError, ...)
Error.stackThe full call path down to the failure site
Every at ... lineOne function in the call chain
OrderThe failure site on top, its caller below

Common mistakes

  • Treating error.stack as a standard with a guaranteed format. It is a de facto convention, and the text differs between engines, so parsing it is unreliable.
  • Thinking the stack is captured at throw time. It is actually recorded at new Error(...), so creating an error early and throwing it later is a bad idea.
  • Logging only e.message and losing e.stack, which leaves nothing to trace the failure with.
  • Throwing a string or a plain object instead of an Error, because such a value has no stack at all.
  • Being surprised by short stacks in asynchronous code. Pass the original error along through cause so the context is not lost.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.