Salesforce Dictionary - Free Salesforce GlossarySalesforce Dictionary
All errors
Apex

System.ListException: List index out of bounds: <N>

You indexed a `List` past its size. Apex doesn't return null for out-of-range — it throws. The most common pattern is `[SELECT ... LIMIT 1][0]` against an empty list.

Also seen asList index out of bounds·ListException·System.ListException: List index·list out of bounds apex

Apex lists are zero-indexed and bounded. myList[5] requires myList.size() > 5. Otherwise: ListException: List index out of bounds.

The classic offender

Account a = [SELECT Id FROM Account WHERE Name = 'Acme' LIMIT 1][0];

If no Account named Acme exists, the SOQL returns an empty list. [0] throws.

The right pattern:

List<Account> hits = [SELECT Id FROM Account WHERE Name = 'Acme' LIMIT 1];
if (hits.isEmpty()) return null;     // or throw a meaningful error
Account a = hits[0];

Always check size before indexing. The isEmpty() / size() check costs nothing.

Closely related but different

System.QueryException: List has no rows for assignment to SObject (see that page) fires when you assign a SOQL result directly to an SObject variable and the result is empty. ListException fires when you index a List variable past its end. Different exception type, same family of bug.

When the index is computed

for (Integer i = 0; i <= myList.size(); i++) {   // ❌ off-by-one
    process(myList[i]);
}

The condition i <= size() runs one iteration past the end. Fix to < not <=. Or use the foreach form, which can't be off-by-one:

for (Object item : myList) process(item);

A subtle source: parallel arrays

Code that maintains two lists in parallel (e.g., keys and values indexed together) breaks the moment one list grows differently from the other:

List<Id> keys = new List<Id>();
List<String> values = new List<String>();
// ... populate ...
for (Integer i = 0; i < keys.size(); i++) {
    String v = values[i];   // 💥 if values.size() < keys.size()
}

Replace with a Map<Id, String> whenever the two lists should stay in sync. Maps are inherently consistent.

The defensive test

Add a unit test that exercises the empty-list path explicitly:

@isTest static void emptyResultIsHandledGracefully() {
    // Org has no record matching the filter
    Account a = MyClass.findByName('NoSuchRecord');
    System.assertEquals(null, a, 'Empty result should return null, not throw');
}

The test that runs against a populated sandbox passes; the one that runs against an empty test context catches the bug before production.

Related dictionary terms