reading-stack-traces
verified4b8bbbcc-ebf0-4dfe-956a-b3570346fcda
Use when reading Python, JavaScript, Go, or Java stack traces — read from bottom up, find YOUR code first, extract the reproduction from the trace.
Metadata
Skill file
# Reading Stack Traces
**Use when** you're staring at a stack trace or traceback and need to find the actual bug — not just the symptom.
## The Universal Algorithm
1. **Read the LAST line first** — that's the actual error type + message
2. **Scan bottom-to-top** — find the first frame that is YOUR code, not the framework/library
3. **Extract the inputs** — what values were in flight when it failed?
4. **Build the repro** — the trace gives you the failing call; write a test with those inputs
## Python Traceback — Worked Example
```text
Traceback (most recent call last):
File "/app/views.py", line 42, in handle_request # (4) YOUR CODE — start here
result = service.process(data, user_id=None)
File "/app/service.py", line 18, in process # (3) YOUR CODE
return _validate(user)["status"]
File "/app/service.py", line 9, in _validate # (2) YOUR CODE
return {"status": db.lookup(user.id)} # (1) THE CRASH
AttributeError: 'NoneType' object has no attribute 'id'
```
Read order:
1. **Bottom**: `AttributeError` — something is `None`
2. **First your code** (bottom-up): `_validate` line 9 — `user.id` where `user` is `None`
3. **Why is user None?**: `process` line 18 — `_validate(user)` and `user` came from `data`
4. **Why is data wrong?**: `handle_request` line 42 — `user_id=None` passed explicitly
### The fix is at line 42, not line 9
The bug: `handle_request` passes `user_id=None`. The crash at line 9 is the symptom.
```python
# Wrong fix (treats symptom):
def _validate(user):
if user is None:
return {"status": "unknown"} # hides the real bug!
# Right fix (treats cause):
# In handle_request: guard against user_id=None BEFORE calling process
```
## JavaScript Stack Trace
```text
TypeError: Cannot read properties of undefined (reading 'name')
at User.render (src/components/UserCard.js:15:24) # (3) YOUR CODE
at ReactComponent (node_modules/react/...) # (2) framework — skip
at renderPage (src/pages/Dashboard.js:42:5) # (1) YOUR CODE — caller
```
Read: `User.render` line 15, `something.name` where `something` is `undefined`. Look at line 42 in Dashboard to find why `undefined` was passed.
## Go Panic Trace
```text
panic: runtime error: index out of range [5] with length 3
goroutine 1 [running]:
main.processBatch(0xc000012000, 0x5, 0x3)
/app/main.go:67 +0x2a5
main.main()
/app/main.go:24 +0x9f
```
Read: `processBatch` line 67 — a slice index `5` on a slice of length `3`. Look at the caller (`main` line 24) to find where the bad index originated.
## Decision Table: Where to Look First
| Trace Characteristic | First Frame to Read |
|---|---|
| One frame in your code, rest in framework | Your code — the error is in what you passed to the framework |
| Multiple frames in your code | The BOTTOM-MOST frame in your code (closest to the crash) |
| All framework/library frames | Your code called the library wrong — check the call site |
| Recursion overflow | The function name repeating — check the base case |
| Thread/interrupt/goroutine | Find the goroutine line — it's the entry point for that thread |
## Extracting a Repro from the Trace
The trace IS a repro — it tells you the exact function call that failed:
```python
# From trace: File "service.py", line 18, in process -> _validate(user)
# user was None because data from handle_request had user_id=None
# Repro test:
def test_process_with_none_user():
"""Reproduces AttributeError from traceback"""
with pytest.raises(AttributeError):
service.process({"items": []}, user_id=None)
```
## Guardrails
- NEVER fix the line the trace points to until you understand why the bad value got there
- Read the error TYPE, not just the message. `AttributeError` vs `KeyError` vs `ValueError` tells you different things
- Framework frames (Django, React, Express) are almost never where the bug lives
- The first frame of your code is the symptom, not necessarily the cause
## Pitfalls
| Pitfall | Fix |
|---------|-----|
| Reading top-down (frame 1 → N) | Always read bottom-up: error type first |
| Stopping at the crash line | Ask: "What value caused this?" then "Why was that value there?" |
| Fixing at the crash instead of the origin | Trace back to where the bad value was created |
| Skimming the error type | `ValueError` ≠ `TypeError` ≠ `KeyError` — the type IS the clue |
| Ignoring "Caused by" chains | Python's "During handling of the above exception..." — read the ORIGINAL |
## Verify / Checklist
- [ ] Last line (error type + message) understood
- [ ] First frame of YOUR code (not framework) identified
- [ ] Input values at that frame extracted (the "what")
- [ ] Origin of bad input traced (the "why")
- [ ] Reproduction test written from the trace
- [ ] Fix targets the origin, not just the crash site
Attached files
No attached files.