{"record":{"id":"d6fb3f6c4740c886","repo":"mongodb/laravel-mongodb","slug":"aggregation-nesting-exceeds-the-maximum-of-d-levels","errorCode":null,"errorMessage":"Aggregation nesting exceeds the maximum of %d levels.","messagePattern":"Aggregation nesting exceeds the maximum of (.+?) levels\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"src/Tools/DatabaseQuery.php","lineNumber":133,"sourceCode":"        }\n\n        if ($operation === 'aggregate') {\n            // Check nested write ops recursively with conservative allow list\n            $this->ensureNoNestedWriteInAggregation($command['pipeline'] ?? []);\n        }\n\n        return $connection->getDatabase()->command($command)->toArray();\n    }\n\n    /**\n     * @param array<int, array<string, mixed>> $pipeline\n     *\n     * @throws InvalidArgumentException\n     */\n    private function ensureNoNestedWriteInAggregation(array $pipeline, int $level = 1): void\n    {\n        if ($level > self::MAX_NESTING_LEVEL) {\n            throw new InvalidArgumentException(sprintf('Aggregation nesting exceeds the maximum of %d levels.', self::MAX_NESTING_LEVEL));\n        }\n\n        if (count($pipeline) > self::MAX_STAGES_PER_PIPELINE) {\n            throw new InvalidArgumentException(sprintf('A pipeline exceeds the maximum of %d stages.', self::MAX_STAGES_PER_PIPELINE));\n        }\n\n        // These aggregation stages may contain nested aggregation pipelines\n        // and must be checked for write ops recursively.\n        $supportsNestedWrites = ['facet', 'lookup', 'unionWith'];\n\n        // Every known read-only aggregation stage. Kept as an allow list (rather than\n        // denying $merge/$out) so that write stages added in the future are rejected by\n        // default. See https://www.mongodb.com/docs/manual/reference/mql/aggregation-stages\n        $allowList = array_merge($supportsNestedWrites, [\n            'addFields',\n            'bucket',\n            'bucketAuto',\n            'changeStream',","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/mongodb/laravel-mongodb/blob/0634653039468ceb0268a69192bdac64469ed043/src/Tools/DatabaseQuery.php#L115-L151","documentation":"The aggregate pipeline nesting (recursion through $facet, $lookup and $unionWith sub-pipelines) went deeper than 100 levels, the library's guard mirroring BSON's 100-level nesting limit. The check runs recursively in ensureNoNestedWriteInAggregation, incrementing `$level` for each nested sub-pipeline.","triggerScenarios":"An aggregate command whose sub-pipelines inside $facet/$lookup/$unionWith recurse or stack more than 100 levels deep, e.g. 101 nested $lookup.pipeline levels or a $facet whose facet pipelines each nest further past the limit.","commonSituations":"Machine- or LLM-generated pipelines with pathological nesting; programmatically built recursive facet/lookup trees; deeply generated JSON from another tool pasted in as the command.","solutions":["Flatten the pipeline: reduce nesting of $facet/$lookup/$unionWith sub-pipelines to under 100 levels.","Restructure recursive lookups as iterative queries or use $graphLookup for recursive traversal instead of nested $lookup stages.","Validate pipeline depth client-side before invoking the tool."],"exampleFix":"// before: 101 nested stages, each [['$lookup' => ['from' => 'x', 'pipeline' => <next>]]]\n// after\n{\"aggregate\": \"users\", \"pipeline\": [{\"$lookup\": {\"from\": \"orders\", \"localField\": \"_id\", \"foreignField\": \"user_id\", \"as\": \"orders\"}}, {\"$limit\": 10}]}","handlingStrategy":"validation","validationCode":"function pipelineDepth(array $pipeline): int {\n    $max = 1;\n    foreach ($pipeline as $stage) {\n        $name = str_replace('$', '', (string) array_key_first($stage));\n        $body = $stage[array_key_first($stage)];\n        if ($name === 'facet' && is_array($body)) {\n            foreach ($body as $sub) { $max = max($max, 1 + pipelineDepth($sub)); }\n        } elseif (in_array($name, ['lookup', 'unionWith'], true) && is_array($body) && isset($body['pipeline'])) {\n            $max = max($max, 1 + pipelineDepth($body['pipeline']));\n        }\n    }\n    return $max;\n}\nif (pipelineDepth($pipeline) > 100) { throw new \\InvalidArgumentException('pipeline too deeply nested'); }","typeGuard":null,"tryCatchPattern":"try {\n    $result = $tool->handle($request);\n} catch (\\InvalidArgumentException $e) {\n    if (str_contains($e->getMessage(), 'nesting exceeds')) {\n        // flatten or restructure the pipeline, then retry\n    }\n}","preventionTips":["Prefer $graphLookup over recursive $lookup chains.","Cap generated $facet/$lookup/$unionWith nesting well below 100 levels.","Review machine-generated pipelines for pathological recursion before sending."],"tags":["mongodb","aggregation","nesting-limit","mcp-tool"],"backgroundTag":"value-out-of-range","analyzedSha":"0634653039468ceb0268a69192bdac64469ed043","analyzedAt":"2026-09-15T02:56:37.067Z","contentChangedAt":"2026-09-15T02:56:37.067Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}