Debugging
Stack traces are your roadmap to finding bugs. Learn to read them in any language and you will fix errors faster than you ever thought possible.
What Is a Stack Trace?
A stack trace (also called a traceback in Python) is a snapshot of the call stack at the moment an error occurs. It lists every function that was active when the program crashed, from the most recent call at the top to the entry point at the bottom.
Think of it as a breadcrumb trail. The error happened at the top frame, and each frame below it shows how the program got there. Your job is to follow the trail from top to bottom to understand the sequence of events.
Understand how the call stack works
Reading a JavaScript Stack Trace
Here is a typical JavaScript stack trace:
TypeError: Cannot read properties of undefined (reading 'name')
at getUserName (app.js:42)
at processUser (app.js:28)
at handleRequest (app.js:15)
at app.js:5
How to read it:
- First line: The error type (
TypeError) and message (Cannot read properties of undefined). This tells you WHAT went wrong. - Second line: The function where the error occurred (
getUserName) and the exact line number (42). This tells you WHERE it happened. - Subsequent lines: The call chain.
handleRequestcalledprocessUserwhich calledgetUserName. This tells you HOW you got there.
In Node.js, stack traces show the full file path. In the browser, they show the source URL. Use your browser's DevTools to click on any frame and jump to the source code.
Reading a Python Traceback
Python tracebacks are more verbose but follow the same logic:
Traceback (most recent call last):
File "app.py", line 15, in handle_request
process_user(data)
File "app.py", line 28, in process_user
name = get_user_name(user_id)
File "app.py", line 42, in get_user_name
return user["name"]
KeyError: 'name'
Read it from bottom to top for the error cause, or top to bottom for the call chain:
- Last line: The error type (
KeyError) and what caused it ('name'). - Lines above: The call chain with file names and line numbers. Each
Fileentry shows where the next function was called from.
Reading a Java Stack Trace
Java stack traces are detailed and include thread information:
java.lang.NullPointerException: Cannot invoke method getName() on null object
at com.app.UserService.getUserName(UserService.java:42)
at com.app.UserService.processUser(UserService.java:28)
at com.app.Controller.handleRequest(Controller.java:15)
at com.app.Main.main(Main.java:5)
Each line shows the full class path, method name, and file location. The at keyword separates frames. Java stack traces are the most detailed of any language and include the exact line numbers within parentheses.
Reading a C Segfault Stack
C stack traces come from tools like gdb or addr2line. Without debug symbols, you see raw memory addresses:
Program received signal SIGSEGV, Segmentation fault.
#0 0x0000555555555149 in getUser (user=0x0) at app.c:42
#1 0x00005555555551a3 in process (data=0x555555602010) at app.c:28
#2 0x00005555555551f1 in main (argc=2, argv=0x7fffffffe388) at app.c:15
To get readable output, compile with debug symbols (gcc -g) and use gdb or addr2line. The #0 frame is where the crash happened. Frames numbered #1, #2, etc. are the callers.
Common Patterns in Stack Traces
Null Reference / Undefined
The most common error in every language. The stack trace shows the exact line where you tried to access a property or method on null/undefined. Look at what the variable was supposed to contain and trace back to where it should have been assigned.
Type Error
You passed the wrong type to a function or used an operator on an incompatible type. The stack trace points to the operation, and the message tells you what type was expected vs what was received.
Stack Overflow
The error message itself says "stack overflow" or "maximum call stack size exceeded". The stack trace will show the same function repeated many times, indicating infinite recursion. Find the missing or incorrect base case.
Using console.trace() and debugger
You do not have to wait for an error to see the call stack. In JavaScript:
function importantFunction() {
console.trace("Called from:"); // prints current call stack
debugger; // pauses execution and opens DevTools at this line
}
console.trace() prints the call stack at that point without throwing an error. The debugger statement pauses execution so you can inspect variables and the call stack in DevTools. Use these to understand how your code reaches a particular point.
Techniques for Debugging Deep Call Chains
- Start from the error message: The error type and message tell you what went wrong. Focus on the first frame in YOUR code, not library code.
- Read upward from the bottom: In some tracebacks, reading from the entry point (bottom) to the error (top) helps you understand the flow.
- Add logging at key points: If the call chain is long, add
console.logor print statements to trace the flow. - Use conditional breakpoints: In DevTools, set breakpoints that only trigger when a condition is true. This helps narrow down when the error occurs.
- Check the line number: Stack traces include exact line numbers. Jump to that line and read the surrounding code.
- Look for async boundaries: In JavaScript, async function call chains sometimes appear differently. Check if the error crosses an
awaitboundary.
Tools for Stack Analysis
| Language | Tool | Purpose |
|---|---|---|
| JavaScript | Browser DevTools | Interactive stack inspection, breakpoints, variable watch |
| JavaScript | console.trace() | Print call stack without pausing |
| JavaScript | Error.stack property | Programmatic access to stack trace string |
| Python | pdb / ipdb | Interactive debugger with step-through |
| Python | traceback module | Programmatic access to traceback frames |
| Java | IDE debugger (IntelliJ, Eclipse) | Full stack inspection with variable evaluation |
| Java | Thread.dumpStack() | Print current thread's stack |
| C | gdb | Low-level stack inspection and core dumps |
| C | addr2line | Convert memory addresses to file and line |
Try the interactive call stack visualizer
Key Takeaway
A stack trace is not a wall of confusing text. It is a precise map of what happened. Read the error message first, find the first frame in your code, and follow the call chain downward. With practice, you will be able to identify the root cause of most bugs from the stack trace alone.
Frequently Asked Questions
How do I read a stack trace effectively?
Start at the top. The first line is the error type and message. Below that, find the first frame in YOUR code (not library or framework code). That frame is where the error actually happened. The frames below it show what called that function. Read from top to bottom to trace the path that led to the error.
What is the difference between a stack trace and a stack overflow?
A stack trace is diagnostic output showing the chain of function calls at a point in time. A stack overflow is an error that occurs when the call stack exceeds its memory limit, usually due to infinite recursion. A stack overflow will produce a stack trace when it crashes.
Why are stack traces sometimes minified or missing?
In production JavaScript, source maps translate minified code back to original source. Without source maps, you see minified function names and line numbers. Enable source maps in your build process to get readable stack traces. In C, missing debug symbols produce addresses instead of function names.