System.QueryException: Aggregate query has too many rows for direct assignment, use FOR loop
What does this error mean?
When an aggregate SOQL query (one using GROUP BY or aggregate functions) returns more than 2,000 rows, Salesforce does not allow direct assignment to a List<AggregateResult> variable. The direct assignment would require loading all 2,000+ rows into memory simultaneously, which Salesforce guards against. The solution is to use a SOQL for-loop, which processes aggregate results in chunks.
Common Causes
1. High-cardinality GROUP BY field
Grouping by a field with many distinct values (e.g., GROUP BY OwnerId in a large org with thousands of users, or GROUP BY External_Id__c on a high-volume object) produces more than 2,000 groups.
2. No LIMIT clause on aggregate query
An aggregate query without a LIMIT that returns an unexpectedly large number of distinct groups as data grows over time.
How to Fix It
Solution 1: Use a SOQL for-loop for large aggregate results
// ❌ BAD — fails if more than 2,000 groups
List<AggregateResult> results = [
SELECT OwnerId, SUM(Amount) total
FROM Opportunity
GROUP BY OwnerId
];
// ✅ GOOD — for-loop handles any number of groups
Map<Id, Decimal> totalByOwner = new Map<Id, Decimal>();
for (AggregateResult ar : [
SELECT OwnerId, SUM(Amount) total
FROM Opportunity
GROUP BY OwnerId
]) {{
totalByOwner.put(
(Id) ar.get('OwnerId'),
(Decimal) ar.get('total')
);
}}
Solution 2: Add a LIMIT clause if you only need top results
// If you only need the top N groups, add LIMIT
List<AggregateResult> top10 = [
SELECT OwnerId, SUM(Amount) total
FROM Opportunity
GROUP BY OwnerId
ORDER BY SUM(Amount) DESC
LIMIT 10
];