I'm writing a batch class that I'm hoping can utilize some existing trigger code. However, the trigger code uses the addError method, which causes my batch to fail with the message System.FinalException: SObject row does not allow errors. I'm running this method in a try-catch block so I can gather all errors and send them in an email to the running user, but it appears I cannot catch this exception at all. I built the following POC to confirm the behavior:

public class TestFinalExceptionBatch implements Database.Batchable<SObject> {

    public Database.QueryLocator start(Database.BatchableContext bc){
        return Database.getQueryLocator('SELECT Id FROM Foo__c');

    public void execute (Database.BatchableContext bc, List<Foo__c> scope){

        try {
            for (Foo__c f : scope){
        } catch (System.FinalException f){
        } catch (Exception e) {

    public void finish(Database.BatchableContext bc){}

And my fears are confirmed - I cannot catch this exception. The crazy thing is though, I run similar code from execute anonymous, and it doesn't throw an error at all.

So, a few questions here: First, what would be the best way to overcome this problem? I'm hoping to change the existing code as little as possible. My first thought is using Trigger.isExecuting but is there a better way?

Second, System.FinalException doesn't seem to appear at all in the Apex Exceptions Documentation. Any idea why? It appears to be uncatchable like a LimitException, and I'd love to have some confirmation of that from the docs. And along with that, can anyone shed some light on addError and when it will cause an error and when it won't? And is it always uncatchable?

  • Are you able to edit the code? You can check system.isBatch().
    – Adrian Larson
    Feb 27, 2017 at 21:21

1 Answer 1


You'd have to actually perform a DML operation to call the trigger if you want to handle addError. Something like this would work:

public void execute(Database.BatchableContext context, Foo__c[] scope) {
    Database.SaveResult[] results = Database.update(scope, false);
    // Process errors here

You can only use addError in a DML or Visualforce context. You can't use the method in any context where you couldn't possibly give an error back to the user. You may need to modify your trigger or handlers to support running in batch mode using System.isBatch() if you only want to reuse part of the trigger code; you definitely need to avoid using addError when you're not in a context that supports addError.

System.FinalException is probably an oversight in the documentation; it's been around probably at least as long as triggers have been out of beta, possibly earlier. Like LimitException, it can't be caught. It's not documented as to why, but I suspect because it represents a logic error that should never arise (i.e. the developer must not write logic that would cause this exception to arise).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .