{"record":{"id":"b5ece32a4b43ceae","repo":"JuliusBrussee/caveman","slug":"cave-delegate-depth-exceeded","errorCode":"cave_delegate_depth_exceeded","errorMessage":"cave_delegate_depth_exceeded","messagePattern":"cave_delegate_depth_exceeded","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agents/delegate/caveman-delegate-mcp.mjs","lineNumber":141,"sourceCode":"    },\n    required: [\"task\"],\n  },\n};\n\nasync function handle(msg) {\n  const { id, method, params } = msg;\n  if (method === \"initialize\") {\n    return {\n      protocolVersion: params?.protocolVersion || PROTOCOL_FALLBACK,\n      capabilities: { tools: { listChanged: false } },\n      serverInfo: { name: \"caveman-delegate\", version: \"0.1.0\" },\n    };\n  }\n  if (method === \"tools/list\") return { tools: [TOOL] };\n  if (method === \"ping\") return {};\n  if (method === \"tools/call\") {\n    if (params?.name !== TOOL.name) throw new Error(`unknown tool: ${params?.name}`);\n    if (process.env.CAVEMAN_DELEGATE_CHILD === \"1\") throw new Error(\"cave_delegate_depth_exceeded\");\n    const task = params?.arguments?.task;\n    if (!task || typeof task !== \"string\") throw new Error(\"cave_delegate_missing_task\");\n    const r = await runWorker(task, params?.arguments?.cwd);\n    const lines = [r.out || \"(worker produced no output)\"];\n    if (r.usage) {\n      const u = r.usage;\n      let usageLine = `delegate usage (measured, provider-reported): input ${u.input + u.cacheRead + u.cacheWrite} (cacheRead ${u.cacheRead}, cacheWrite ${u.cacheWrite}), output ${u.output}`;\n      if (u.cost > 0) usageLine += `, cost $${u.cost.toFixed(4)}`;\n      lines.push(\"---\", usageLine);\n    }\n    if (r.code !== 0) {\n      lines.push(\"---\", `worker exit ${r.code}: ${r.err.slice(-500)}`);\n      return { content: [{ type: \"text\", text: lines.join(\"\\n\") }], isError: true };\n    }\n    return { content: [{ type: \"text\", text: lines.join(\"\\n\") }] };\n  }\n  if (method?.startsWith(\"notifications/\")) return undefined;\n  throw Object.assign(new Error(`method not found: ${method}`), { code: -32601 });","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/agents/delegate/caveman-delegate-mcp.mjs#L123-L159","documentation":"Delegate workers are spawned with CAVEMAN_DELEGATE_CHILD=1 in their environment (agents/delegate/caveman-delegate-mcp.mjs:72). If the caveman_delegate tool is invoked again inside such a worker, the server throws cave_delegate_depth_exceeded immediately: delegation is limited to exactly one level so worker sessions cannot recursively spawn more workers (a fork-bomb and runaway-cost guard).","triggerScenarios":"Calling caveman_delegate from an agent session that was itself started by caveman_delegate (the env var is inherited); or exporting CAVEMAN_DELEGATE_CHILD=1 in the shell that later launches the MCP server.","commonSituations":"A delegate worker's task prompt instructs it to delegate sub-work; the env var was set globally for testing and forgotten; wrapper scripts that pass the full parent environment through.","solutions":["Move the delegation up: only the top-level session calls caveman_delegate, and worker tasks are written to be self-contained.","Run several independent delegate calls in parallel from the top level instead of nesting them.","If the var was inherited unintentionally and this really is a top-level session, unset CAVEMAN_DELEGATE_CHILD in the shell before starting the MCP server.","Restructure the workflow so the worker returns intermediate results and the orchestrator issues the next delegate call itself."],"exampleFix":"# before: nested delegation (inside a delegate worker)\nserver.callTool('caveman_delegate', { task: 'now do part 2' }) // throws\n# after: orchestrator does both calls itself\nawait server.callTool('caveman_delegate', { task: 'part 1' })\nawait server.callTool('caveman_delegate', { task: 'part 2' })","handlingStrategy":"validation","validationCode":"// Before invoking the delegate, check the depth guard's marker.\nif (process.env.CAVEMAN_DELEGATE_CHILD === '1') {\n  throw new Error('already inside a delegate worker; nesting caveman_delegate is not allowed');\n}\nawait client.request({\n  method: 'tools/call',\n  params: { name: 'caveman_delegate', arguments: { task: '...' } },\n});","typeGuard":null,"tryCatchPattern":"try {\n  await callCavemanDelegate(task);\n} catch (e) {\n  if (String(e?.message).includes('cave_delegate_depth_exceeded')) {\n    // Restructure: return this subtask's result to the top-level orchestrator\n    // and issue the next caveman_delegate call from there.\n  }\n  throw e;\n}","preventionTips":["Write delegate worker tasks as self-contained; never instruct the worker to call caveman_delegate itself.","Keep CAVEMAN_DELEGATE_CHILD out of global shell profiles — it is an internal marker set by the server.","Fan out parallel subtasks from the top-level session instead of nesting delegations."],"tags":["mcp","recursion-guard","delegate","environment-variable"],"backgroundTag":"recursion-depth-limit","analyzedSha":"766dce6b1394ebb56a3090748d5a0240a5aefb36","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}