Process Automation and Logic · Topic 7 of 7

Exception Handling and Custom Exceptions

Apex exception handling for Platform Developer: try, catch and finally, built-in exceptions, DmlException methods, custom exceptions, addError and limits.

  • 4 min
In a nutshell
  • try, catch (specific first), finally (always runs).
  • LimitException cannot be caught; design to avoid it.
  • Custom exceptions extend Exception and end with Exception.

What it is

An exception is an error that stops normal execution. Apex lets you catch most exceptions with try, catch and finally, recover or log, and throw your own custom exceptions for business errors.

An unhandled exception rolls back the transaction and shows an error to the user or caller. A handled one lets the code decide what happens next.

Code runsException thrownCatch blocklog, recover, rethrowFinallyalways runs

How it works

Syntax

try {
    Account a = [SELECT Id FROM Account WHERE Name = :name];
    a.Rating = 'Hot';
    update a;
} catch (QueryException e) {
    System.debug('No single match: ' + e.getMessage());
} catch (DmlException e) {
    System.debug('Save failed: ' + e.getDmlMessage(0));
} catch (Exception e) {
    System.debug('Unexpected: ' + e.getTypeName());
} finally {
    System.debug('Always runs');
}

Catch blocks are checked in order, so put specific types before the generic Exception.

Common built-in exceptions

ExceptionTypical cause
DmlExceptionA DML statement fails: validation rule, required field, duplicate
QueryExceptionA query assigned to one sObject returns zero or several rows
NullPointerExceptionUsing a null variable, such as calling a method on it
ListExceptionIndex out of bounds, such as list[5] on a short list
SObjectExceptionReading a field that was not queried
TypeExceptionA failed type conversion, such as Integer.valueOf('abc')
CalloutExceptionAn HTTP callout fails or times out
LimitExceptionA governor limit is exceeded; cannot be caught

Useful exception methods

MethodReturns
getMessage()The error message
getTypeName()The exception type, such as System.DmlException
getLineNumber(), getStackTraceString()Where it happened
getNumDml()Number of failed rows in a DmlException
getDmlIndex(i)Position in the original list of the i-th failed record
getDmlMessage(i), getDmlFieldNames(i)Message and fields for that failure

Custom exceptions

public class InsufficientStockException extends Exception {}

public static void reserve(Product__c p, Integer qty) {
    if (p.Stock__c < qty) {
        throw new InsufficientStockException('Only ' + p.Stock__c + ' left');
    }
}
  • The class must extend Exception and its name must end with Exception.
  • It inherits constructors for no argument, a message, a cause, and a message with a cause.
  • Throw it to signal a business rule failure that the caller can catch by type.

addError versus throwing

NeedUse
Reject one record in a trigger, keep partial-success loads workingrecord.addError('message')
Show a message next to a field on the record pagerecord.Field__c.addError('message')
Stop the whole operation from a service methodThrow a custom exception
Show a friendly error to a Lightning componentThrow an AuraHandledException from the @AuraEnabled method

A worked example

A try block runs insert accts; with 200 accounts, and three fail a validation rule. The catch block for DmlException e runs. None of the 200 accounts are saved by that statement, e.getNumDml() returns 3, and e.getDmlIndex(0) gives the list position of the first failed account. Catching does not save the valid 197. To keep them, use Database.insert(accts, false) and read the SaveResult array.

Exam tip

When a code sample wraps a loop of queries in try and catch, check whether the error is a governor limit. A LimitException skips the catch block and the transaction fails.

How the exam asks about this

  • "Signal a business error the caller can catch by type." A custom exception that extends Exception.
  • "Show a user-friendly message in a Lightning Web Component." AuraHandledException.
  • "Which failed record caused the DmlException?" getDmlIndex().

What to remember for the exam

  • try, catch (specific first), finally (always runs).
  • LimitException cannot be caught; design to avoid it.
  • Custom exceptions extend Exception and end with Exception.
  • getNumDml(), getDmlIndex() and getDmlMessage() describe DML failures.
  • In triggers, prefer addError() to reject specific records.
Watch out

An empty catch block hides failures. At least log the error, or rethrow it, so problems are visible.

Common mistakes

Naming a custom exception StockError. The name must end in Exception, or it does not compile.

Catching an exception to save the valid rows. The DML statement already rolled back all its rows.

Catching the generic Exception first. Later specific catch blocks can never run.

Check yourself

  • What two rules must a custom exception class follow?

    It must extend Exception, and its name must end with Exception.

  • Can a catch block handle System.LimitException?

    No. Limit exceptions cannot be caught, and the transaction fails.

  • What does e.getDmlIndex(0) return?

    The position in the original list of the first record that failed.

Topic 7 of 7 in Process Automation and Logic

Test yourself on Process Automation and Logic

The full Salesforce Platform Developer question bank has 645 questions, with timed practice exams and an explanation for every answer.

This study guide is independent and is not affiliated with, endorsed by, or sponsored by Salesforce. Vendor names, certification names and exam codes are trademarks of their respective owners and are used for identification only.