What Is a Stack Trace? How to Read One to Find a Bug
A stack trace is the ordered list of function calls that were active at the exact moment a program threw an error. It is printed innermost-call-first: the top line is the error itself, and each line below is the function that called the one above it, all the way back to where the program started. Read correctly, a stack trace tells you not just what broke but the precise line where it broke and the path the code took to get there.
What is a stack trace?
When code runs, the language keeps a “call stack” — a record of which function is running, which function called it, and so on. When something throws an error, the runtime captures a snapshot of that stack and prints it. That snapshot is the stack trace.
Each entry in the trace is called a frame, and a frame almost always contains three things:
- The function name — what was executing (or
<anonymous>if it was an unnamed callback). - The file — which source file the function lives in.
- The location — a line and often a column number, written as
file.js:42:17.
The frames are stacked in reverse order of calling: the function that actually threw the error is at the top, and the program's entry point is at the bottom.
How to read a stack trace
The fastest way to turn a trace into a fix is a fixed, repeatable scan. It takes well under a minute once it's a habit.
- Read the top line first. This is the error type and message — for example,
TypeError: Cannot read properties of undefined (reading 'id'). The type tells you the category of failure and the message often names the exact variable or property involved. - Move down to the first frame. The line immediately under the message is where the error was actually thrown. Note the file and line number.
- Find the first frame in your own code. If the top frames point into a library, a framework, or the runtime, keep scanning down until you hit a file you wrote. That boundary — where the trace crosses from library code into your code — is usually where the real bug lives, because it's the last decision your code made before handing bad input to the library.
- Follow the chain downward if needed. If the top frame is a small, generic helper called from many places, walk down the trace to see which caller passed the bad value. The trace is a map of exactly how the program arrived at the failure.
Which line in a stack trace actually matters?
Most traces have far more frames than you need. The two that matter almost every time are:
- The error message at the top — it names the failure and frequently the offending value.
- The topmost frame that lives in your own source — not
node_modules, not the runtime, not framework internals.
A useful rule: the deeper frames (near the bottom) tell you the context — the request, the route, the button click that started it all. The upper frames tell you the cause. When you're hunting a fix, start from the cause end. When you're trying to reproduce, the context end tells you the entry point to trigger.
Reading a JavaScript stack trace: a worked example
Here is a typical browser stack trace:
TypeError: Cannot read properties of undefined (reading 'name')at renderUser (profile.js:88:22)at ProfilePage (profile.js:41:9)at renderWithHooks (react-dom.js:14985:18)at mountIndeterminateComponent (react-dom.js:17811:13)
Reading it: the error is a TypeError, and the message says something tried to read .name on a value that was undefined. The first frame, renderUser (profile.js:88:22), is the throw site — and it's your file, so that's the line to open. The frame below shows it was called from ProfilePage. The two library frames (react-dom.js) are just the framework machinery that ran your component; they're noise for this bug. The fix is almost certainly at profile.js:88: some user object arrived undefined, and the code assumed it wouldn't.
Why your stack trace points to the wrong line
Sometimes the line number in the trace doesn't match what you see in your editor. The common causes are worth knowing before you waste time in the wrong file:
- Minified or bundled code. Production JavaScript is often compressed into a single line, so the trace shows something like
bundle.js:1:84213. A source map maps that position back to your original file and line; most browser devtools apply it automatically when the map is available. - Transpiled or compiled code. TypeScript, JSX, and other build steps shift line numbers between source and output. Again, source maps are the fix.
- Async gaps. With promises,
await, timers, and event handlers, the frame that scheduled the work may not appear above the frame that ran it, so the trace can look shorter than the real logical path. Modern runtimes stitch async traces together, but older or heavily-transformed code may not. - Re-thrown or swallowed errors. If code catches an error and throws a new one, the trace can point at the re-throw site instead of the original cause. Look for a wrapped or “caused by” section, and preserve the original error when you re-throw.
How to include a stack trace in a bug report
A stack trace is one of the highest-value pieces of evidence you can attach to a bug, because it points a developer straight at the failing line. To make it usable:
- Copy the whole trace, not a screenshot of two lines. The full text is searchable and complete; a cropped screenshot loses the frames that show the path in.
- Include the error message verbatim. Don't paraphrase “something about undefined” — the exact type and message are what a developer greps for.
- Pair it with the console and network state. The trace tells you where; the surrounding console errors and failed requests often tell you why the input was bad in the first place.
- Note whether it reproduces. A trace from a one-off is still useful, but tag intermittent ones so triage weights them correctly.
This is exactly the kind of context that's easy to lose when a bug is reported after the fact. Klavity Snap captures the stack trace, console, network requests, and environment at the moment someone flags a bug — so the report arrives with the frame that matters already attached, not reconstructed from memory. If you're standardizing how your team files bugs, a stack trace should be a required field, right next to the steps and the minimal reproducible example.
Key takeaways
- Read top-to-bottom: the top frame is where the error was thrown
- Find the first frame in your own code — skip library and runtime frames
- Keep the error type and message; they narrow the cause before you open the file
- Ship the full trace with the bug report, plus the console and network state
FAQ
How do you read a stack trace?
Start at the top. The first line is the error type and message; the frames below it are the call stack, ordered from where the error was thrown (top) back to the program's entry point (bottom). Scan down to the first frame that references your own source file rather than a library or the runtime — that is usually the line to fix.
What's the difference between a stack trace and an error message?
The error message is one line that says what went wrong (for example, 'TypeError: Cannot read properties of undefined'). The stack trace is the sequence of function calls that shows where and how it went wrong. You need both: the message tells you the failure, the trace tells you the location and the path that got there.
Why does my stack trace point to the wrong line?
Usually because the code is minified or transpiled, so line numbers refer to the built bundle, not your source. A source map maps built lines back to original ones. Async operations, error re-throwing, and swallowed exceptions can also shift the true origin, so read the whole trace rather than trusting only the top frame.
Catch bugs the moment a human sees them
Klavity: right-click bug reports, AI personas that review your product, and self-healing tests.
Get started free