Skip to main content

SF-0320 · Coding · Medium

Provide an example of handling generic exceptions as well as specific exceptions using built-in exception types?

✓ Verified by Vikas Singhal · Last reviewed 5/17/2026 · Updated for Spring '26

The pattern: catch specific exception types first, then fall back to a generic Exception. The compiler routes a thrown exception to the first matching catch block, so ordering matters.

A full worked example

public with sharing class AccountUpdater {

    public static void renameAccount(Id accountId, String newName) {
        try {
            // Specific failure modes we want to handle differently
            Account a = [SELECT Id, Name FROM Account WHERE Id = :accountId LIMIT 1];
            a.Name = newName;
            update a;

        } catch (QueryException qe) {
            // Specific: no row, too many rows, malformed SOQL
            System.debug('Lookup failed: ' + qe.getMessage());
            throw new AccountUpdaterException('Account not found: ' + accountId, qe);

        } catch (DmlException de) {
            // Specific: validation rule, required field, duplicate, sharing
            System.debug('Save failed: ' + de.getDmlMessage(0));
            throw new AccountUpdaterException('Could not save account', de);

        } catch (Exception e) {
            // Generic: catches anything else (null pointer, type cast, etc.)
            System.debug('Unexpected error: ' + e.getTypeName() + ' — ' + e.getMessage());
            throw new AccountUpdaterException('Unexpected error renaming account', e);
        }
    }

    public class AccountUpdaterException extends Exception {}
}

Three things to notice:

  1. Specific first. QueryException and DmlException are checked before the wildcard Exception catch. If Exception came first the compiler would refuse to build — unreachable code.
  2. Different reactions per type. A query miss probably means a bad input, so we surface a user-friendly “Not found” message. A DML failure is a save problem, so we surface the field-level error. The generic catch is the safety net for everything we didn’t anticipate.
  3. Wrapping preserves the cause. Re-throwing as a domain-specific AccountUpdaterException keeps callers from depending on Salesforce’s internal types, but passing qe / de / e as the second argument preserves the original stack trace via getCause().

Single specific catch

If you only care about one failure mode:

try {
    insert new Contact(LastName = 'Smith');
} catch (DmlException de) {
    for (Integer i = 0; i < de.getNumDml(); i++) {
        System.debug(de.getDmlFieldNames(i) + ' — ' + de.getDmlMessage(i));
    }
}

getDmlFieldNames(i) gives you the field-level cause for row i, which is gold for showing a useful message to the user.

Single generic catch

For “log everything, fail nothing critical”:

try {
    sendOptionalEmail();
} catch (Exception e) {
    Logger.logException(e); // capture and move on
}

This pattern is for fire-and-forget side effects (analytics pings, cache warmers) — never for primary business logic. Swallowing a critical exception silently is one of the worst bugs you can ship in a multi-tenant environment.

Common follow-ups

  • Can you re-order generic-first? — No. The compiler rejects unreachable catch blocks.
  • How many catches per try? — As many as you have exception types, but only one of each type.
  • Should every method catch? — No. Let exceptions bubble to the boundary (controller, REST endpoint, trigger entry point) and catch there. Catch-too-early hides real bugs.

Verified against: Apex Developer Guide — Catching Different Exception Types. Last reviewed 2026-05-17 for Spring ‘26.