@future method is not static or does not return void

ID

apex.correctness.future_static_void

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Api Design

Language

Apex

Description

Reports a method annotated @future that is declared without static, or whose return type is anything other than void.

Rationale

The platform enqueues a @future call and runs it later, in its own transaction, with no caller left to receive a value and no instance to run against. A signature that promises either is rejected by the compiler, so the class does not save and the whole deployment stops - and because the annotation is the only thing that makes the rule apply, the mistake reads as ordinary code right up to the point where the package fails to install. The same signature compiles perfectly well once the annotation is removed, which is why it survives review.

The following code illustrates the pattern detected by this rule:

public class AccountSyncQueue {

    // FLAGGED: @future method is not static or does not return void
    @future
    public void pushInstance(Set<Id> accountIds) {
        System.debug(accountIds);
    }

    // FLAGGED: @future method is not static or does not return void
    @future(callout=true)
    public static Integer pushAndCount(Set<Id> accountIds) {
        return accountIds.size();
    }

    // FLAGGED: @future method is not static or does not return void
    @future
    private Boolean pushPrivate(Set<Id> accountIds) {
        return accountIds.isEmpty();
    }

    @future
    public static void push(Set<Id> accountIds) {
        System.debug(accountIds);
    }

    @future(callout=true)
    private static void pushWithCallout(Set<Id> accountIds) {
        System.debug(accountIds);
    }

    public Integer countSynchronously(Set<Id> accountIds) {
        return accountIds.size();
    }
}

Remediation

Declare the method static void. Where the caller needs a result, the work does not belong in a @future method: use a Queueable, whose job id can be tracked and whose outcome a Finalizer can act on, and re-query the records inside it instead of returning them.

Configuration

This detector does not need any configuration.

© 2026 Xygeni. All rights reserved