@future method called from an asynchronous entry point

ID

apex.correctness.future_from_async_context

Severity

high

Remediation Complexity

medium

Remediation Risk

medium

Remediation Effort

medium

Resource

Api Usage

Language

Apex

Description

Reports a class implementing Queueable, Schedulable or Database.Batchable whose execute method calls a @future method declared in that same class; a call to a @future method declared in another class is not reported, because the annotation is not visible here.

Rationale

The platform refuses to start one asynchronous job from inside another: a @future call made from Queueable.execute, from Database.Batchable.execute or from Schedulable.execute throws System.AsyncException with "Future method cannot be called from a future or batch method". Nothing in the compiler or the deployment catches it, so the class deploys and the failure only appears when the job runs - in a batch that means the chunk fails after the first records are already committed, and the job finishes with errors that nobody was watching for. Scheduled and queued jobs run without a user in front of them, so the exception lands in the Apex Jobs list rather than in front of whoever caused it.

The following code illustrates the pattern detected by this rule:

// FLAGGED: @future method called from an asynchronous entry point
public class AccountSyncJob implements Queueable, Database.AllowsCallouts {

    private List<Id> accountIds;

    public AccountSyncJob(List<Id> accountIds) {
        this.accountIds = accountIds;
    }

    public void execute(QueueableContext context) {
        pushToErp(accountIds);
    }

    @future(callout=true)
    public static void pushToErp(List<Id> ids) {
        System.debug(ids);
    }
}

Remediation

Move the work out of the @future method into a method the async context can call directly, and let the caller keep the asynchronous boundary it already has. Where the work needs its own transaction, chain a Queueable with System.enqueueJob from finish instead - a batch’s finish may enqueue, and a Queueable may enqueue one successor. Where the @future method exists only to allow a callout, implement Database.AllowsCallouts on the queueable or batch class and make the callout there.

Configuration

This detector does not need any configuration.

© 2026 Xygeni. All rights reserved