System.LimitException: Too many queueable jobs added to the queue: 51
What does this error mean?
Salesforce limits System.enqueueJob() calls to 50 per transaction. If you enqueue a Queueable job for each record in a bulk operation and that operation processes 51+ records, this limit is reached. The error is thrown immediately and the transaction is rolled back.
Note that within a Queueable's own execute() method, you may only enqueue one child job — this is a separate constraint from the 50-per-transaction limit that governs synchronous code.
Common Causes
1. System.enqueueJob() called inside a loop
The same anti-pattern as with @future — enqueuing a job per record in a trigger loop causes this error the moment a bulk operation exceeds 50 records.
2. Multiple independent Queueable dispatches per transaction
Different automation paths (flow + trigger + process builder) each enqueuing separate jobs in the same transaction, totalling more than 50.
How to Fix It
Solution 1: Enqueue one job with a collection of IDs
Design your Queueable to accept and process a Set<Id> so a single enqueue handles all records.
// ❌ BAD — one enqueue per record
for (Account acc : Trigger.new) {{
System.enqueueJob(new MyQueueable(acc.Id));
}}
// ✅ GOOD — one enqueue for all records
System.enqueueJob(new MyQueueable(Trigger.newMap.keySet()));
// Queueable accepts a Set
public class MyQueueable implements Queueable {{
private Set<Id> ids;
public MyQueueable(Set<Id> ids) {{ this.ids = ids; }}
public void execute(QueueableContext ctx) {{
// process all records in ids
}}
}}
Solution 2: Use Batch Apex for very large volumes
If the set of records to process is very large, Batch Apex is a better fit than Queueable. A single Database.executeBatch() call processes any number of records in chunked executions with fresh governor limits per chunk.
Test context note: In Apex tests, System.enqueueJob() is limited to 1 call per test method. If your trigger enqueues multiple jobs, wrap your test in Test.startTest() / Test.stopTest() and use a mock or stub to avoid this constraint.
Pro Tip: Use Limits.getQueueableJobs() to check how many Queueable jobs have been enqueued in the current transaction. Centralise all async dispatching logic in a single handler class to keep the count predictable.