Stack trace reading
A stack trace is a story told backwards. The top frame is where the program
noticed the problem, which is rarely where the problem started. The reflex
to fix the topmost line patches a symptom and leaves the cause. Read the
trace as a map from where you are to your own code that put you there.
Method
- Read the exception type and message first, together.
NullPointer,
KeyError: 'user_id', ECONNREFUSED 127.0.0.1:5432: the type says what
rule broke and the message says on what value. That pair alone often
names the cause before you look at a single frame.
- Find the boundary between your code and the library. Traces run
deepest-call-on-top in most runtimes (Java, JS, Ruby) and
deepest-on-bottom in Python. Scan for the first frame in a path you
own, next to the framework or standard-library frames. The bug usually
sits at that seam, where your data entered someone else's code.
- Separate cause frames from symptom frames. The top frame is where it
threw. The frame that passed the bad value is a few steps down, in your
code. A null dereferenced in a library means your caller handed it null;
walk down until the arguments become yours, and read what you passed.
- Follow "Caused by" and "During handling" to the original. Wrapped
exceptions chain: Java prints
Caused by: deeper, Python prints "The
above exception was the direct cause". The root cause is the last link
in that chain, not the first one printed. Read to the bottom of the chain
before forming a hypothesis.
- Map frames to versions with line numbers. A frame is
file:line for
a reason. Open that exact line at the committed revision, not main.
Minified or optimized stacks need a source map or symbol file first, or
the line numbers point at nothing real.
- Discard async and framework noise deliberately. Event loops,
thread pools, and promise machinery fill traces with frames you did not
write and cannot fix. Collapse them mentally and keep only the frames in
your packages: that shortened trace is the one to reason about.
Signals
- Can you name the exact line in your own code that created the bad value?
- Did you read to the end of the "Caused by" chain, not just the first line?
- Does the frame you plan to fix hold the cause, or just where it surfaced?
Boundaries
Stack overflow, memory corruption, and optimized release builds produce
traces that lie: inlined frames vanish, tail calls collapse, and the top
frame may be a victim of earlier damage. There, trust a core dump or a
sanitizer over the printed trace, and see the core-dumps skill.
1---2name: stack-trace-reading3description: Read a stack trace to find the cause frame instead of stopping at the symptom on top. Use when an exception, panic, or error dump lands and you need to locate the line that is actually wrong.4---56# Stack trace reading78A stack trace is a story told backwards. The top frame is where the program9noticed the problem, which is rarely where the problem started. The reflex10to fix the topmost line patches a symptom and leaves the cause. Read the11trace as a map from where you are to your own code that put you there.1213## Method14151. **Read the exception type and message first, together.** `NullPointer`,16 `KeyError: 'user_id'`, `ECONNREFUSED 127.0.0.1:5432`: the type says what17 rule broke and the message says on what value. That pair alone often18 names the cause before you look at a single frame.192. **Find the boundary between your code and the library.** Traces run20 deepest-call-on-top in most runtimes (Java, JS, Ruby) and21 deepest-on-bottom in Python. Scan for the first frame in a path you22 own, next to the framework or standard-library frames. The bug usually23 sits at that seam, where your data entered someone else's code.243. **Separate cause frames from symptom frames.** The top frame is where it25 threw. The frame that passed the bad value is a few steps down, in your26 code. A null dereferenced in a library means your caller handed it null;27 walk down until the arguments become yours, and read what you passed.284. **Follow "Caused by" and "During handling" to the original.** Wrapped29 exceptions chain: Java prints `Caused by:` deeper, Python prints "The30 above exception was the direct cause". The root cause is the last link31 in that chain, not the first one printed. Read to the bottom of the chain32 before forming a hypothesis.335. **Map frames to versions with line numbers.** A frame is `file:line` for34 a reason. Open that exact line at the committed revision, not `main`.35 Minified or optimized stacks need a source map or symbol file first, or36 the line numbers point at nothing real.376. **Discard async and framework noise deliberately.** Event loops,38 thread pools, and promise machinery fill traces with frames you did not39 write and cannot fix. Collapse them mentally and keep only the frames in40 your packages: that shortened trace is the one to reason about.4142## Signals4344- Can you name the exact line in your own code that created the bad value?45- Did you read to the end of the "Caused by" chain, not just the first line?46- Does the frame you plan to fix hold the cause, or just where it surfaced?4748## Boundaries4950Stack overflow, memory corruption, and optimized release builds produce51traces that lie: inlined frames vanish, tail calls collapse, and the top52frame may be a victim of earlier damage. There, trust a core dump or a53sanitizer over the printed trace, and see the core-dumps skill.