Helpful NullPointerException Messages
For years, a NullPointerException came with no message at all, just a line number. On a line like student.address.city.length(), that left you guessing: was student null? address? city? Since Java 14, the JVM writes a helpful message that names the exact reference that was null and the action that failed. It's on by default, and it turns many debugging sessions into a ten-second fix.
Messages That Point at the Problem
public class Helpful {
static class Address { String city; }
static class Student { Address address; }
public static void main(String[] args) {
Student s = new Student();
s.address = new Address();
try {
System.out.println(s.address.city.length());
} catch (NullPointerException e) {
System.out.println(e.getMessage());
}
s.address = null;
try {
System.out.println(s.address.city);
} catch (NullPointerException e) {
System.out.println(e.getMessage());
}
int[] marks = null;
try {
System.out.println(marks[0]);
} catch (NullPointerException e) {
System.out.println(e.getMessage());
}
Integer bonus = null;
try {
int b = bonus;
} catch (NullPointerException e) {
System.out.println(e.getMessage());
}
}
}javac -g Helpful.java
java HelpfulCannot invoke "String.length()" because "s.address.city" is null
Cannot read field "city" because "s.address" is null
Cannot load from int array because "marks" is null
Cannot invoke "java.lang.Integer.intValue()" because "bonus" is nullHow it works
Each message has two parts:
- What Java tried to do: call
length(), read the fieldcity, load from an array, or unbox anInteger. - Which reference was null:
"s.address.city","s.address","marks","bonus". In the first case,sands.addresswere fine; onlycitywas null. No guessing needed.
Getting Variable Names
The messages show local variable names only when the class was compiled with debug information for local variables. Without it, you'll see placeholders:
| Compiled with | First message |
|---|---|
javac -g Helpful.java (IDEs and Maven/Gradle do this by default) | because "s.address.city" is null |
Plain javac Helpful.java | because "<local1>.address.city" is null |
A lambda's captured variable, no -g | because "<parameter1>" is null |
Fields like address and city are always named; only local variables and parameters need -g.
All the Actions It Describes
| Message starts with | The null reference was used to... |
|---|---|
Cannot invoke "X.m()" | Call a method |
Cannot read field "f" | Read a field |
Cannot assign field "f" | Write a field |
Cannot load from int array | Read an array element |
Cannot store to int array | Write an array element |
Cannot read the array length | Use .length |
Cannot throw exception | throw null |
Cannot enter synchronized block | synchronized (null) |
Common Mistakes
Ignoring the message and reading only the line number. The message usually names the culprit directly.
Catching NPEs instead of fixing them. The message tells you which reference needs a null check or initialization.
Being puzzled by <local1>. Compile with -g, or run from an IDE.
Practice
- Easy: Write a line that throws
Cannot read the array length because "names" is nulland fix it. - Harder: Build a three-level chain
order.customer.phone.trim()where onlyphoneis null. Predict the message, then run it.
Key Takeaways
Was this tutorial helpful?