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.
The recommended alternative
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
| From | Call @future? |
|---|---|
Batch start | No |
Batch execute | No |
Batch finish | No |
Batch finish calling Queueable | Yes |
Schedulable execute | Yes (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? —
finishruns 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.