Skip to main content

SF-0331 · Scenario · Medium

Can we call batch apex in trigger?

✓ Verified by Vikas Singhal · Last reviewed 5/17/2026 · Updated for Spring '26

Yes — you can call Database.executeBatch() from a trigger, and it compiles, deploys, and runs without complaint. But you almost never should, and the interview pivots quickly to why.

What works

trigger AccountTrigger on Account (after update) {
    if (Trigger.isExecuting && Trigger.size > 1000) {
        Database.executeBatch(new AccountRefreshBatch(Trigger.newMap.keySet()), 200);
    }
}

The platform happily accepts this. Apex enqueues the batch job and the trigger transaction continues.

Why “yes, but” — three real problems

1. The 5 active batch jobs limit

Salesforce caps an org at 5 active or queued Apex batch jobs at any moment. A trigger that fires on bulk DML can easily breach this:

  • A data loader pushes 50,000 records in 250 batches of 200.
  • Each chunk of 200 enters the trigger.
  • Each trigger calls Database.executeBatch().
  • After the 5th call, every subsequent one throws System.AsyncException: You have exceeded the maximum number of 5 batch jobs allowed.

Result: the user’s bulk load fails partway through, with a confusing async error.

2. The 50 async-call ceiling per transaction

A single transaction can enqueue at most 50 async jobs total (across @future, Queueable, and Database.executeBatch). A trigger firing batch on each of 200 records hits the wall at record #51.

3. The batch starts whenever the scheduler decides

Batch jobs aren’t queued for “immediate” execution — they sit in the Apex Flex Queue and start when the platform has capacity. The trigger returns instantly; the batch might run 30 seconds or 30 minutes later. If users expect “the moment I save, the work happens” they’ll be confused.

The right async tool from a trigger

Most cases that look like “call batch from a trigger” should actually use Queueable Apex instead. Queueable:

  • Has a higher cap — 50 jobs enqueued from a trigger context.
  • Starts much faster — typically within seconds.
  • Supports chaining one job to the next.
  • Accepts complex object state through its constructor.
trigger AccountTrigger on Account (after update) {
    System.enqueueJob(new AccountRefreshQueueable(Trigger.newMap.keySet()));
}

If the workload genuinely needs millions of records scanned, batch is the right answer — but launch it from a scheduled class or a button, not the OLTP trigger path.

What you cannot call from a trigger

Async typeCallable from trigger?Notes
@futureYesUp to 50/transaction. Can’t pass SObjects — only primitives.
QueueableYes50/transaction. Preferred for trigger-launched async.
Database.executeBatchYes, with caveats5 active total org-wide.
System.scheduleYes (Apex 47+)Schedules a Schedulable to run later.
Inbound calloutNoCallout from a trigger is not allowed — go async first.

A safer pattern when you must call batch

If business rules truly require launching a batch from a trigger, gate it:

trigger AccountTrigger on Account (after update) {
    if (!BatchLauncher.alreadyLaunchedThisTx) {
        if (System.AsyncInfo.getCurrentQueueableStackDepth() == 0) {
            // Only launch batch from the original transaction, not chained
            try {
                Database.executeBatch(new AccountRefreshBatch(), 200);
                BatchLauncher.alreadyLaunchedThisTx = true;
            } catch (System.AsyncException e) {
                // Already 5 batches running. Defer to a queueable instead.
                System.enqueueJob(new AccountRefreshQueueable(...));
            }
        }
    }
}

The try/catch matters: in production you cannot assume the 5-job slot is free, so plan a fallback.

What interviewers are really looking for

The answer is “yes, but rarely the right tool.” Strong candidates name: (1) the 5 active batch jobs org limit, (2) the 50 async calls per transaction limit, (3) Queueable as the better default for trigger-launched async, (4) that you can’t do callouts directly from a trigger — you must go async first. Bonus signal: mention that scheduled classes can also be invoked from a trigger via System.schedule(), but you need a unique job name each time.

Verified against: Apex Developer Guide — Using Batch Apex, Async Apex Limits. Last reviewed 2026-05-17.