mongodb/laravel-mongodb · error · InvalidArgumentException
A pipeline exceeds the maximum of
Error message
A pipeline exceeds the maximum of %d stages.
What it means
One of the aggregation pipelines in the command (top-level or a sub-pipeline inside $facet/$lookup/$unionWith) has more than 1000 stages, matching MongoDB's documented pipeline stage limit. The library rejects it up front instead of letting the server fail.
Solutions
- Reduce the number of stages to 1000 or fewer, merging consecutive $match stages into one with $and or $in.
- Combine consecutive expression stages (e.g. many $addFields into one $addFields document).
- Filter data before the pipeline (e.g. in application code or with a $match on indexed fields) rather than chaining hundreds of stages.
Example fix
// before: pipeline with 1500 entries, one {"$match": {"status": "s"}} per status
// after
{"aggregate": "users", "pipeline": [{"$match": {"status": {"$in": ["a", "b", "c"]}}}, {"$limit": 100}]} Defensive patterns
Strategy: validation
Validate before calling
$countStages = fn (array $p): int => array_sum(array_map(
fn ($s) => ((string) array_key_first($s) === '$facet' && is_array($s['$facet']))
? array_sum(array_map($countStages, $s['$facet']))
: 1,
$p
));
if (count($pipeline) > 1000) { throw new \InvalidArgumentException('pipeline has more than 1000 stages'); } Try / catch
try {
$result = $tool->handle($request);
} catch (\InvalidArgumentException $e) {
if (str_contains($e->getMessage(), 'maximum of 1000 stages')) {
// merge/collapse stages and retry
}
} Prevention
- Merge consecutive $match stages using $and / $in.
- Combine repeated $addFields into a single stage document.
- Count pipeline entries programmatically before dispatching large generated pipelines.
When it happens
Trigger: An aggregate command with a `pipeline` array longer than 1000 entries, or any nested sub-pipeline (inside $facet, $lookup.pipeline, $unionWith.pipeline) exceeding 1000 stages.
Common situations: Programmatically generated pipelines that unroll thousands of $match/$addFields stages; bulk-converted query builders; AI-generated pipelines that repeat stages instead of using $in or aggregation expressions.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- Aggregation nesting exceeds the maximum of
- Only read aggregation stages are allowed. Found
- Please pass a valid MongoDB command
- Only read commands are allowed
- Database is not properly configured.
AI-assisted analysis of mongodb/laravel-mongodb@0634653039 (2026-09-15).
Data as JSON: /api/errors/3b5fd4d47d93e764.
Report an issue: GitHub.
Appendix: source
Thrown at src/Tools/DatabaseQuery.php:137
$this->ensureNoNestedWriteInAggregation($command['pipeline'] ?? []);
}
return $connection->getDatabase()->command($command)->toArray();
}
/**
* @param array<int, array<string, mixed>> $pipeline
*
* @throws InvalidArgumentException
*/
private function ensureNoNestedWriteInAggregation(array $pipeline, int $level = 1): void
{
if ($level > self::MAX_NESTING_LEVEL) {
throw new InvalidArgumentException(sprintf('Aggregation nesting exceeds the maximum of %d levels.', self::MAX_NESTING_LEVEL));
}
if (count($pipeline) > self::MAX_STAGES_PER_PIPELINE) {
throw new InvalidArgumentException(sprintf('A pipeline exceeds the maximum of %d stages.', self::MAX_STAGES_PER_PIPELINE));
}
// These aggregation stages may contain nested aggregation pipelines
// and must be checked for write ops recursively.
$supportsNestedWrites = ['facet', 'lookup', 'unionWith'];
// Every known read-only aggregation stage. Kept as an allow list (rather than
// denying $merge/$out) so that write stages added in the future are rejected by
// default. See https://www.mongodb.com/docs/manual/reference/mql/aggregation-stages
$allowList = array_merge($supportsNestedWrites, [
'addFields',
'bucket',
'bucketAuto',
'changeStream',
'changeStreamSplitLargeEvent',
'collStats',
'count',
'currentOp',View on GitHub (pinned to 0634653039)