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
try,catch(specific first),finally(always runs).LimitExceptioncannot be caught; design to avoid it.- Custom exceptions extend
Exceptionand end withException.
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.
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
| Exception | Typical cause |
|---|---|
DmlException | A DML statement fails: validation rule, required field, duplicate |
QueryException | A query assigned to one sObject returns zero or several rows |
NullPointerException | Using a null variable, such as calling a method on it |
ListException | Index out of bounds, such as list[5] on a short list |
SObjectException | Reading a field that was not queried |
TypeException | A failed type conversion, such as Integer.valueOf('abc') |
CalloutException | An HTTP callout fails or times out |
LimitException | A governor limit is exceeded; cannot be caught |
Useful exception methods
| Method | Returns |
|---|---|
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
Exceptionand its name must end withException. - 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
| Need | Use |
|---|---|
| Reject one record in a trigger, keep partial-success loads working | record.addError('message') |
| Show a message next to a field on the record page | record.Field__c.addError('message') |
| Stop the whole operation from a service method | Throw a custom exception |
| Show a friendly error to a Lightning component | Throw 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.
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).LimitExceptioncannot be caught; design to avoid it.- Custom exceptions extend
Exceptionand end withException. getNumDml(),getDmlIndex()andgetDmlMessage()describe DML failures.- In triggers, prefer
addError()to reject specific records.
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 withException.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.