Skip to main content

SF-0355 · Scenario · Medium

Can we call future methods from a batch job?

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

No. Calling a @future method from any of a Batch class’s lifecycle methods (start, execute, finish) throws:

System.AsyncException: Future method cannot be called from a future or batch method.

The same rule that blocks future-from-future also blocks future-from-batch. Both are already async; the platform refuses to let an async transaction spawn another future.

The error in practice

public class MyBatch implements Database.Batchable<sObject> {
    public Database.QueryLocator start(Database.BatchableContext ctx) {
        return Database.getQueryLocator('SELECT Id FROM Account');
    }
    public void execute(Database.BatchableContext ctx, List<Account> scope) {
        List<Id> ids = new List<Id>();
        for (Account a : scope) ids.add(a.Id);
        ExternalSyncer.syncAsync(ids); // AsyncException — boom
    }
    public void finish(Database.BatchableContext ctx) {}
}

The chunk fails. The job continues with the next chunk, where it fails again. NumberOfErrors climbs while no work gets done.

Why the rule exists

@future predates Queueable and was capped for the same reasons future-from-future is capped: prevent uncontrolled fan-out. A batch processing 1 million records in 5,000 chunks could enqueue 5,000 future methods if the rule didn’t exist — instantly exhausting async capacity.

Make the work directly inside execute if possible — Batch Apex already runs async, so wrapping more async inside it is usually unnecessary. For callouts:

public class MyBatch implements Database.Batchable<sObject>, Database.AllowsCallouts {
    public Database.QueryLocator start(Database.BatchableContext ctx) { /* ... */ }
    public void execute(Database.BatchableContext ctx, List<Account> scope) {
        Http http = new Http();
        for (Account a : scope) {
            HttpRequest req = new HttpRequest();
            req.setEndpoint('callout:ERP/accounts/' + a.Id);
            req.setMethod('PUT');
            http.send(req);
        }
    }
    public void finish(Database.BatchableContext ctx) { /* ... */ }
}

Implement Database.AllowsCallouts and you have callouts directly inside execute. No future needed.

If you really need it

If you need to defer some work past the batch run, chain a Queueable instead:

public void finish(Database.BatchableContext ctx) {
    // Allowed: Batch finish can enqueue Queueable
    System.enqueueJob(new PostBatchCleanup(ctx.getJobId()));
}

Queueable from Batch is fully supported. Future from Batch is not.

Quick rules

FromCall @future?
Batch startNo
Batch executeNo
Batch finishNo
Batch finish calling QueueableYes
Schedulable executeYes (Schedulable is treated as sync for this rule)

Common interview follow-ups

  • Does the error stop the whole batch? — No, it fails that chunk. Future chunks try again and also fail.
  • Why is the future-from-finish blocked?finish runs inside the batch context. It’s still considered batch.
  • Can I call Queueable from finish? — Yes, and it’s the standard chaining pattern.

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