Reading Error Messages Like a Pro
Beginners panic at red text. Pros read it. The difference is a 5-step routine that takes about a minute and saves about an hour.
The Habit That Separates Beginners From Pros
Watch any beginner debug for ten minutes and you will see the same anti-pattern: red text appears, panic ensues, the student starts changing things at random, and the error gets worse. Watch a senior do it and you will see them read the error first - slowly, completely - before touching any code.
That difference is mostly habit, not talent. The good news is that habit is teachable. The five-step routine below converts the error is scary into the error told me which line to look at. It works for any language and any error type.
Step 1: Read the Last Line
Stack traces in most languages are reverse-chronological. The actual error type and message are on the LAST line. The lines above are how the program got there.
Read the last line slowly. There are two pieces of information: the type of error (TypeError, ReferenceError, KeyError) and the message (x is not a function). Both matter.
Step 2: Match the Type to a Mental Category
Every common error type maps to a category of mistake:
TypeError- wrong-type operation. undefined is not a function means you called something that is not a function.ReferenceError/NameError- used a name that does not exist.SyntaxError- code does not parse. Look on or just before the reported line.IndexError- accessed past the end of an array. See off-by-one.KeyError- missing key in a dict (Python).NullPointerException- touched a null value (Java).
Step 3: Open the Line Number
The error tells you the file and line. Open it. Read three lines: the line itself, one above, one below. 80 percent of the time the bug is visible in those three lines.
Step 4: Form a Hypothesis (Aloud)
Before changing anything, say what you think happened: I think the API returned undefined and I tried to call .map on it. Even a wrong hypothesis is faster than random changes.
If your hypothesis includes the words because of magic or for no reason, go back to step 1. The error has a cause. You just have not seen it yet.
Step 5: Verify With One Print
Add one print of whatever you suspect is wrong. console.log("user:", user). Run again. Compare actual vs expected. If they match, fix it. If they do not, you have eliminated a possibility, which is real progress.
Resist the urge to add five prints. One print isolates one question.
A Tour of Errors You Will See This Week
- TypeError: undefined is not a function. Typo or method does not exist on this type.
- TypeError: Cannot read property X of undefined. The object is undefined.
- SyntaxError: Unexpected token. Missing or extra paren/brace. Look one line above.
- ReferenceError: x is not defined. Typo or scope. Check spelling first.
- IndexError / KeyError. See our array bugs page.
- RangeError: Maximum call stack size exceeded. Infinite recursion. You forgot the base case.
- UnhandledPromiseRejection. Async failed and you did not catch it. Add try/catch.
- NetworkError. Network call failed. Check URL, method, CORS.
Build the Habit
Try our debugging concept page and the Spot the Bug mini-game. Both drill error-pattern recognition.
For the broader debugging mindset, see How to Debug Without Panicking.
External references: MDN's complete list of JS errors and the Python exceptions hierarchy.
Language-by-Language Quirks
Three habits that will save you when switching languages:
- Python tracebacks read top-to-bottom. The first frame is where the error originated; the last is where it propagated to. The error type and message are at the very bottom, on the last line.
- JavaScript stack traces read top-to-bottom too in modern browsers. The error message is on top in the dev tools, and the stack frames extend below it. Click line numbers to jump to the source.
- Java exceptions are verbose by design. NullPointerException with a 50-line trace looks intimidating. Find the line in your code (not the framework code), open it, and start there.
For deep understanding, the Oracle exceptions tutorial is the canonical Java reference. For Python, the official exceptions hierarchy is short and worth a slow read.
When the Error Is Worse Than No Error: Silent Failures
Some bugs do not throw errors. They quietly produce wrong results. These are harder to debug because the routine above does not trigger. Common patterns:
- JavaScript returning undefined from a function that forgot to return.
- Truthy strings like "false" or "0" passing if-checks they should not.
- Float comparison failing because
0.1 + 0.2 !== 0.3. - String "5" sorting before string "10" alphabetically when you expected numeric order.
The defense against silent failures is unit tests. Even one test per function catches most of these. The debugging concept page has a deeper section on systematically catching silent failures.
Dev Tools You Should Know
Three tools that pay for themselves within a week:
- Browser DevTools - Chrome's docs are the best reference. Network, Console, Elements, Sources tabs all earn their keep.
- Python pdb -
import pdb; pdb.set_trace()drops you into an interactive debugger at that line. Typenfor next,sto step into,p varto print. - Java IDEs (IntelliJ, VS Code) - both offer click-to-set-breakpoint and step-through debugging. Once you use it, you stop debugging Java with print statements.
For the broader debugging routine, see how to debug without panicking.