{"record":{"id":"7dc50519bd6d8246","repo":"can1357/oh-my-pi","slug":"cannot-spawn-blockedagent-agent-from-within-its","errorCode":null,"errorMessage":"Cannot spawn ${blockedAgent} agent from within itself (recursion prevention). Use a different agent type.","messagePattern":"Cannot spawn (.+?) agent from within itself \\(recursion prevention\\)\\. Use a different agent type\\.","errorType":"validation","errorClass":"StructuredSubagentError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/task/structured-subagent.ts","lineNumber":226,"sourceCode":"\t\tthrow new StructuredSubagentError(\n\t\t\t\"preflight\",\n\t\t\t\"Subagent isolation, apply, and merge controls are unavailable in plan mode.\",\n\t\t);\n\t}\n}\n\nfunction assertDepthAndSpawnAllowed(request: StructuredSubagentRequest, agentName: string): void {\n\tconst taskDepth = request.session.taskDepth ?? 0;\n\tconst maxDepth = request.session.settings.get(\"task.maxRecursionDepth\") ?? 2;\n\tif (!canSpawnAtDepth(maxDepth, taskDepth)) {\n\t\tthrow new StructuredSubagentError(\n\t\t\t\"preflight\",\n\t\t\t`Cannot spawn another agent at task depth ${taskDepth}; maximum depth is ${maxDepth}.`,\n\t\t);\n\t}\n\tconst blockedAgent = request.blockedAgent ?? $env.PI_BLOCKED_AGENT;\n\tif (blockedAgent && blockedAgent === agentName) {\n\t\tthrow new StructuredSubagentError(\n\t\t\t\"preflight\",\n\t\t\t`Cannot spawn ${blockedAgent} agent from within itself (recursion prevention). Use a different agent type.`,\n\t\t);\n\t}\n\tconst spawnPolicy = resolveSpawnPolicy(request.session.getSessionSpawns());\n\tif (!spawnPolicy.enabled || (spawnPolicy.allowedAgents !== null && !spawnPolicy.allowedAgents.includes(agentName))) {\n\t\tthrow new StructuredSubagentError(\n\t\t\t\"preflight\",\n\t\t\t`Cannot spawn '${agentName}'. Allowed: ${spawnPolicy.allowedErrorText}`,\n\t\t);\n\t}\n}\n\n/**\n * Resolve every policy shared by task and eval before allocating artifacts or\n * dispatching work. Callers translate {@link StructuredSubagentError} into\n * their own wire-level error surface.\n */","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/task/structured-subagent.ts#L208-L244","documentation":"Thrown in preflight to prevent an agent from spawning itself. The blocked agent name comes from the request's `blockedAgent` field or the `PI_BLOCKED_AGENT` environment variable; if it equals the requested `agentName`, `assertDepthAndSpawnAllowed` refuses the spawn.","triggerScenarios":"A subagent whose name is recorded as blocked (or running under PI_BLOCKED_AGENT=that name) calls the task tool with its own agent name; e.g. an 'implementer' agent trying to spawn 'implementer'.","commonSituations":"Recursive prompts that re-invoke the same agent definition; copying an agent's task call into that agent's own prompt; PI_BLOCKED_AGENT left set in the environment from a previous nesting level.","solutions":["Use a different agent type for the nested call (e.g. spawn a 'reviewer' instead of the same agent)","Unset or correct the PI_BLOCKED_AGENT environment variable if it is stale","Restructure the workflow so the agent's sub-steps are handled by distinct agents"],"exampleFix":"// before (inside implementer agent)\nawait task({ agent: \"implementer\", prompt });\n// after\nawait task({ agent: \"reviewer\", prompt });","handlingStrategy":"validation","validationCode":"const blocked = request.blockedAgent ?? process.env.PI_BLOCKED_AGENT;\nif (blocked && blocked === agentName) {\n  throw new Error(`${agentName} cannot spawn itself; pick another agent`);\n}\nawait task({ agent: agentName, blockedAgent: blocked });","typeGuard":null,"tryCatchPattern":"try {\n  await task(req);\n} catch (e) {\n  if (e instanceof StructuredSubagentError && e.message.includes(\"recursion prevention\")) {\n    return task({ ...req, agent: alternateAgentName });\n  }\n  throw e;\n}","preventionTips":["Never put an agent's own name in its own prompt's task calls","Set PI_BLOCKED_AGENT deliberately and document it in nested-agent templates","Maintain an agent-to-delegate mapping for self-spawn situations"],"tags":["subagent","recursion","self-spawn","preflight"],"backgroundTag":"recursive-agent-spawn-blocked","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}