How can I cause side effects outside an execution context?

Update, Summer '17:

Platform Events are now GA. Note the following from the docs:

Platform Events and Transactions
Unlike custom objects, platform events aren’t processed within database transactions in the Force.com platform. As a result, publishing platform events can’t be rolled back. Note the following:

  • The allOrNoneHeader API header is ignored when publishing platform events through the API.
  • The Apex setSavepoint() and rollback() Database methods aren’t supported with platform events.

So you could publish an event with the required information and have a corresponding Apex trigger subscribed to the event to store the persistent message.

I've expanded the idea out a bit in Can I store information even if the trigger throws an exception?


Wrap the contents of the batch execute method in a try catch and for any caught exceptions throw a new exception with the most important information at the start of the message property. Ideally this will show up in the AsyncApexJob.ExtendedStatus.

This will only help if the exception is of a type that can be caught.


You could abuse the logging mechanism. Use a scheduled task to keep a TraceFlag active for the user running the batch job.

In the Batch finish method pull the log contents if NumberOfErrors is greater than zero. Then painfully extract the missing information.

You could potentially combine this with the above approach and a System.debug(LoggingLevel.Error, e.getMessage()).


Better yet, use a Checkpoint in the code to capture the contents of the exception. Then extract the Exception message from the ApexExecutionOverlayResult using the ToolingAPI from Apex.


I wrote a bit on my blog last week about how I was using the Tooling api to effectively create an "eval()" method. You could replace the transaction mechanism with a similar eval mechanism. The "eval'd" transaction code would still roll everything back, but the get message would be available for the eval exception to return.

see http://codefriar.wordpress.com

MB: So instead of busting out (after DML) to handle the error, it'd bust out to handle the DML itself.

Tooling.Api.Client client = new Tooling.Api.Client();
String command = 'TheClass.thingThatDoesCalloutsAndDmlAndStuff();';

//exec anon maintains 'automatic rollback' behaviour from the uncaught exceptions
Tooling.Api.ExecuteAnonymousResult result = client.executeAnonymous(command);

//do something with message
String exception = result.exceptionMessage;

The way we have resolved (or worked around) this in our package is basically by taking transaction control away from the platform.

First we set a savepoint, then we do the processing, and if an exception occurs we manually rollback, and then we process the error (store it, send an email, ...).

An example of this can be seen at work in the run method of our TDTM_TriggerHandler.

We do it that way because there really isn't any other good alternative. The way I understand it is that outbound calls, emails, chatter posts, etc, are put in queues, and if an unhandled exception occurs they all get pulled from the queue. This makes sense, since an unhandled exception means that there was an error in the transaction, and the platform doesn't have a way of knowing if the outbound call, email, or whatever, has to do with error handling. It has to treat everything as business logic that needs to be rolled back so that the transaction has no side-effects, since there was an error. Also, for completeness, using addError in one of the records has the same effect as an unhandled exception in terms of rollbacks.