AfterInsert.ContinueOnError
Logs and swallows a Writer's or Dispatcher's exception, so later handlers run and the insert goes on. A Writer also gets a private unit of work, so a failure discards only its own writes.
Interface
apex
public class AfterInsert {
public interface ContinueOnError {
}
}AfterInsert.ContinueOnError has no methods: implementing it is the whole opt-in.
Example
apex
public with sharing class ContactWriter implements AfterInsert.Writer, AfterInsert.ContinueOnError {
public Boolean writeOnAfterInsertWhen(TriggerHandler.InsertRecord record) {
return record.isNotNull(Contact.AccountId);
}
public void writeOnAfterInsert(TriggerHandler.InsertRecord record, TriggerHandler.UnitOfWork unitOfWork) {
unitOfWork.toInsert(new Task(WhatId = ((Contact) record.getNewSObject()).AccountId, Subject = 'Review contact'));
}
}cls
public with sharing class AccountWelcomeTaskWriter implements AfterInsert.Writer, AfterInsert.ParentQuery, AfterInsert.ContinueOnError {
public Map<SObjectField, TriggerHandler.ParentFields> queryParentsOnAfterInsert() {
return new Map<SObjectField, TriggerHandler.ParentFields>{ Account.OwnerId => TriggerHandler.ParentFields.with(User.Name, User.Email) };
}
public Boolean writeOnAfterInsertWhen(TriggerHandler.InsertRecord record) {
return record.startsWith(Account.Type, 'Customer');
}
public void writeOnAfterInsert(TriggerHandler.InsertRecord record, TriggerHandler.UnitOfWork unitOfWork) {
Account accountRecord = (Account) record.getNewSObject();
User owner = (User) record.getNewParent('Owner');
unitOfWork.toInsert(
new Task(
WhatId = record.getId(),
OwnerId = accountRecord.OwnerId,
Subject = 'Onboarding call - ' + accountRecord.Name,
Description = 'New customer assigned to ' + owner?.Name + ' (' + owner?.Email + '). Confirm the billing address and the primary contact.',
ActivityDate = Date.today().addDays(3),
Priority = 'High',
Status = 'Not Started'
)
);
}
}Good to Know
- The rest of the handler is skipped. After an exception, its remaining records and its Finalizer do not run.
- Add a Logger. Without a
TriggerOrchestrator.Logger, a swallowed error leaves no trace. See Errors & Logging. - Some errors still fail the insert.
TriggerOrchestratorExceptionandTriggerHandler.TriggerHandlerExceptionare logged and rethrown.System.LimitExceptionand theFinalExceptionfrom writing to the row cannot be caught. - Only the handler's own work. A failure in
bypassOnAfterInsertWhen(),queryParentsOnAfterInsert()or the shared commit still fails the insert. - The private unit costs its own statements. It commits right after the Writer, before the shared unit, and is never merged with it.
