Skip to main content

SF-0351 · Scenario · Medium

What if we have the requirement to work with sObjects in the future method?

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

The future method API forbids sObject parameters, but you almost always need to operate on records. The canonical workaround is pass a List<Id> and re-query inside the method.

The pattern

public class CaseEscalation {

    // Trigger-side: capture the Ids you care about and hand them off
    public static void escalate(List<Case> casesToEscalate) {
        Set<Id> ids = new Set<Id>();
        for (Case c : casesToEscalate) ids.add(c.Id);
        escalateAsync(new List<Id>(ids));
    }

    @future
    public static void escalateAsync(List<Id> caseIds) {
        // Re-query inside the future method
        List<Case> cases = [
            SELECT Id, Status, Priority, OwnerId, AccountId, Account.Name
            FROM Case WHERE Id IN :caseIds
        ];

        for (Case c : cases) {
            c.Priority = 'High';
            c.Status = 'Escalated';
        }
        update cases;
    }
}

The trigger captures Ids, the future re-queries. You get the freshest field values, including any changes other transactions made after the trigger fired.

Why re-query rather than pass primitives for each field?

You might think — why not just pass List<Id> plus a List<String> of new statuses? You can, but it gets messy fast:

ApproachProblem
Pass Ids only, re-queryClean. Always fresh data.
Pass parallel primitive lists (Ids + Statuses + …)Hard to keep in sync; breaks if you add a field
JSON-serialize a wrapper and pass as StringWorks but ugly; lose compile-time safety

Re-querying is the idiomatic answer.

Watch the SOQL limit

If your future method re-queries 50,000+ Ids, you’ll hit governor limits. Chunk the work:

public class BulkCaseProcessor {
    @future
    public static void processChunk(List<Id> chunk) { /* up to 10,000 safe */ }

    public static void processMany(List<Id> allIds) {
        Integer chunkSize = 5000;
        for (Integer i = 0; i < allIds.size(); i += chunkSize) {
            Integer end = Math.min(i + chunkSize, allIds.size());
            List<Id> chunk = new List<Id>();
            for (Integer j = i; j < end; j++) chunk.add(allIds[j]);
            processChunk(chunk);
        }
    }
}

Each call to processChunk is its own future invocation with its own governor limits. Just remember: you can enqueue at most 50 future calls per transaction.

When the workaround isn’t enough

If your numbers are large enough that chunking gets awkward, switch to:

  • Queueable Apex — accepts sObjects and complex types directly, no Id-list dance.
  • Batch Apex — for >50,000 records, where you need automatic chunking across transactions.

Common interview follow-ups

  • Why not pass the entire List<Account>? — Compiler rejects it. @future parameters must be primitives or primitive collections.
  • Will the future method see DML committed in the trigger? — Yes. By the time the future runs, the parent transaction is committed.
  • What if a record was deleted between trigger and future? — Your re-query simply won’t return it. Handle the missing-row case gracefully.

Verified against: Apex Developer Guide — Future Methods. Last reviewed 2026-05-17.