Search
How to Read Error Messages and Stack Traces in Python, JavaScript and Java
Every programmer sees error messages every day. The difference between a beginner and an experienced developer is not that the experienced one makes fewer mistakes. It is that they read the error message and find the problem in a minute, while the beginner copies the red text into a search box, or worse, starts changing random lines and hoping.
The good news: error messages and stack traces follow a pattern, and once you know where to look, they almost always point to the exact line that broke. In this guide we will read three real errors, one each from Python, JavaScript (Node.js) and Java, and then go through a simple routine you can use for any language, including PHP and C#. All three examples were run on the versions noted below, and the error output is exactly what the terminal printed.
The Three Questions Every Error Answers
No matter the language, an error message tells you three things:
- What went wrong: the error type and message, such as
ZeroDivisionError: division by zeroorTypeError: Cannot read properties of undefined. - Where it happened: the file name and line number.
- How the program got there: the stack trace, the list of function calls that were active when the error happened.
The trick is knowing where each language puts these three answers, because they don't all use the same order.
Example 1: Reading a Python Traceback
This script computes each student's average grade. Ana's average prints fine, then the program crashes on Ben:
def average(scores):
return sum(scores) / len(scores)
def report(students):
for name, scores in students.items():
print(f"{name}: {average(scores):.2f}")
students = {
"Ana": [90, 85, 92],
"Ben": [],
}
report(students)
Output (Python 3.13.5):
Ana: 89.00
Traceback (most recent call last):
File "/tmp/demo/grades.py", line 14, in <module>
report(students)
~~~~~~^^^^^^^^^^
File "/tmp/demo/grades.py", line 7, in report
print(f"{name}: {average(scores):.2f}")
~~~~~~~^^^^^^^^
File "/tmp/demo/grades.py", line 2, in average
return sum(scores) / len(scores)
~~~~~~~~~~~~^~~~~~~~~~~~~
ZeroDivisionError: division by zero
How to read it: Python prints the traceback with the most recent call last, as the first line says. So start at the bottom:
- The last line is the what:
ZeroDivisionError: division by zero. - The line above it is the where:
grades.py, line 2, inaverage. Recent Python versions even underline the exact operator (^under the/). - Reading upward gives the how:
averagewas called byreporton line 7, which was called from line 14.
Now ask: why is len(scores) zero? Because Ben's list is empty. The bug is not really in the math; it's that the code never planned for a student with no grades yet. One fix:
def average(scores):
if not scores:
return 0.0
return sum(scores) / len(scores)
def report(students):
for name, scores in students.items():
print(f"{name}: {average(scores):.2f}")
students = {
"Ana": [90, 85, 92],
"Ben": [],
}
report(students)
Ana: 89.00 Ben: 0.00
Whether 0.00 or "No grades yet" is the right answer is a design decision, but now it is a decision instead of a crash.
Example 2: Reading a JavaScript (Node.js) Error
This code prints each user's city. The second user has no address:
function getCity(user) {
return user.address.city.toUpperCase();
}
function printUsers(users) {
for (const user of users) {
console.log(user.name, "-", getCity(user));
}
}
printUsers([
{ name: "Ana", address: { city: "Manila" } },
{ name: "Ben" },
]);
Output (Node.js v24.22.1):
Ana - MANILA
/tmp/demo/app.js:2
return user.address.city.toUpperCase();
^
TypeError: Cannot read properties of undefined (reading 'city')
at getCity (/tmp/demo/app.js:2:23)
at printUsers (/tmp/demo/app.js:7:33)
at Object.<anonymous> (/tmp/demo/app.js:11:1)
at Module._compile (node:internal/modules/cjs/loader:1929:14)
at Object..js (node:internal/modules/cjs/loader:2060:10)
at Module.load (node:internal/modules/cjs/loader:1651:32)
at Module._load (node:internal/modules/cjs/loader:1443:12)
at wrapModuleLoad (node:internal/modules/cjs/loader:261:19)
at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5)
at node:internal/main/run_main_module:33:47
Node.js v24.22.1
How to read it: JavaScript does the opposite of Python. The most important information is at the top:
- The first lines show the file and line (
app.js:2), the code itself, and a caret^pointing at the spot. - Then the what:
TypeError: Cannot read properties of undefined (reading 'city'). Read this carefully. It does not saycityis undefined. It says the thing before.city, which isuser.address, is undefined. - The
at ...lines are the stack trace, newest call first.app.js:2:23means line 2, column 23. - Lines that start with
node:internalbelong to Node.js itself. You can almost always ignore them. Look for the first lines that mention your files.
The fix is to handle users without an address, for example with optional chaining (?.) and a default value (??):
function getCity(user) {
return user.address?.city?.toUpperCase() ?? "NO ADDRESS";
}
function printUsers(users) {
for (const user of users) {
console.log(user.name, "-", getCity(user));
}
}
printUsers([
{ name: "Ana", address: { city: "Manila" } },
{ name: "Ben" },
]);
Ana - MANILA Ben - NO ADDRESS
In the browser, the same error appears in the DevTools Console tab (press F12). Click the file name on the right of the message, and it takes you straight to the line.
Example 3: Reading a Java Stack Trace
This program adds up the units of a student's subjects, but one entry was typed as a word:
package com.school;
import java.util.List;
public class Enrollment {
static int totalUnits(List<String> subjects) {
int total = 0;
for (String s : subjects) {
String units = s.split(":")[1];
total += Integer.parseInt(units.trim());
}
return total;
}
public static void main(String[] args) {
List<String> subjects = List.of("IT 311: 3", "IT 312: 3", "PE 3: two");
System.out.println("Total units: " + totalUnits(subjects));
}
}
Output (JDK 21.0.13):
Exception in thread "main" java.lang.NumberFormatException: For input string: "two" at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67) at java.base/java.lang.Integer.parseInt(Integer.java:662) at java.base/java.lang.Integer.parseInt(Integer.java:778) at com.school.Enrollment.totalUnits(Enrollment.java:10) at com.school.Enrollment.main(Enrollment.java:17)
How to read it: Like JavaScript, Java puts the what at the top: java.lang.NumberFormatException: For input string: "two". That alone almost solves the problem: something tried to turn the text "two" into a number.
Then read the at lines from top to bottom, and skip the ones that are not your code. The first three lines start with java.base/, which is the Java library itself. The bug is almost never there. The first line in your own package is:
at com.school.Enrollment.totalUnits(Enrollment.java:10)
Line 10 is Integer.parseInt(units.trim()), called from main on line 17. The fix depends on the situation: correct the data ("PE 3: 2"), or catch NumberFormatException and show a friendly message when the value comes from user input.
In bigger Java projects, you may also see Caused by: further down the trace. When you see it, scroll to the last Caused by:. That is usually the real root problem, and everything above it is the error being wrapped and re-thrown.
Where to Look First: A Cheat Sheet
| Language | Error message | Your line |
|---|---|---|
| Python | Last line | Just above it (the last File ... line N in your code) |
| JavaScript / Node.js | Top | First at line with your file name |
| Java | First line (and the last Caused by:) | First at line in your own package |
| PHP | The Fatal error or Uncaught ... line | The file and line in the message (in file.php:12 or on line 12), then the #0, #1 lines under "Stack trace" |
| C# / .NET | First line (exception type and message) | First at line in your namespace, with :line N |
A 5-Step Debugging Routine
- Read the whole message slowly. Say it out loud if you have to. "Cannot read properties of undefined (reading 'city')" is a full sentence that tells you what was undefined.
- Find your line. Skip framework and library lines until you reach a file you wrote.
- Check the values on that line. Add a temporary
print(),console.log()orSystem.out.println()just before it, or set a breakpoint in your IDE, and look at the actual data. The bug is usually a value you assumed was there. - Ask "why is it like that?" In all three examples the crash line was fine. The real cause was data the code didn't expect: an empty list, a missing address, a typo in the input.
- Search smart. If you still need help, search for the error type and the general message, not your variable names or file paths. For example, search
NumberFormatException For input string, not your whole trace.
Asking for Help (Including AI Assistants)
When you post in a forum, a group chat or an AI coding assistant, include three things: the full error message and stack trace (not a cropped screenshot), the few lines of code around the failing line, and what you expected to happen. Remove passwords, API keys and database connection strings first. A question like that usually gets a correct answer on the first try, while "my code is not working, please help" gets more questions back.
And when someone (or something) suggests a fix, make sure you understand why the error happened before you paste it. Otherwise the same bug will come back next week in a different file.
Conclusion
An error message is not the computer yelling at you. It is a bug report written for you, with the type, the place and the path that led there. Learn where each language puts those three answers, find your own line in the stack trace, and check the data on that line. Do that consistently and you will spend far less time stuck, and a lot more time building.