System.NullPointerException: Attempt to de-reference a null object
You tried to read a field, call a method, or index into something that turned out to be null. Apex doesn't have safe-navigation by default, so any `obj.field` blows up the moment `obj` is null.
Also seen asAttempt to de-reference a null object·System.NullPointerException·FATAL_ERROR System.NullPointerException
The Apex runtime says you de-referenced null. That phrasing trips up newcomers — "de-reference" just means "look something up on this thing." Translation: you wrote x.y or x.method() or x[0], and x was null at runtime.
The five usual suspects
-
A SOQL result you assumed had a row.
[SELECT Id FROM Account WHERE Name = 'Foo' LIMIT 1]returns an empty list, but you wroteAccount a = [SELECT ... LIMIT 1]and got aQueryExceptioninstead. Then you "fixed" it by changing to[SELECT ...][0]— and now you get a NullPointerException on the empty list. Or you queried a parent:Contact c.Account.Nameis null whenever the contact has no AccountId. -
A trigger context map you forgot to populate.
Trigger.oldMapis null inbefore insertandafter insert. Reading from it during inserts throws. -
A field you forgot to query. Apex returns
nullfor any field not in the SELECT clause — even if it has a value in the database. Chasing a chain likeopp.Account.Owner.Emailrequires every link in the chain to be in the SELECT. -
A static variable that was never initialised.
private static Map<Id, Account> CACHE;is null, not an empty map, until somebody assigns it. -
Apex 8.0+ safe navigation that the rest of the codebase isn't using.
account?.Namereturns null instead of throwing — but if the next operation expects a string, you've just moved the bug downstream.
How to nail down which one
Apex stack traces include the line number. Open the class at that line and read the left side of the dot. The thing immediately before the . is what's null. Then ask: where was that supposed to be assigned?
If it's a SOQL-derived value, log the query result size right before you use it:
List<Account> hits = [SELECT Id, Name FROM Account WHERE Name = :term];
System.debug('hits: ' + hits.size());
if (hits.isEmpty()) return; // or throw a domain-specific error
Account a = hits[0];
If it's a relationship field, query the parent fields explicitly:
// Wrong: opp.Account is null because we never asked for it.
Opportunity opp = [SELECT Id, Name FROM Opportunity WHERE Id = :oppId];
String accName = opp.Account.Name; // boom
// Right.
Opportunity opp = [SELECT Id, Name, Account.Name FROM Opportunity WHERE Id = :oppId];
String accName = opp.Account?.Name ?? 'Unknown';
The defensive pattern
For anything coming from outside your method, default to a guard at the top:
public static Decimal totalAmount(List<OpportunityLineItem> items) {
if (items == null || items.isEmpty()) return 0;
Decimal sum = 0;
for (OpportunityLineItem oli : items) {
if (oli.TotalPrice != null) sum += oli.TotalPrice;
}
return sum;
}
The cost of a one-line guard is negligible. The cost of a NullPointerException in production, on a daily scheduled job, at 3 AM, is a phone call.
