JuliusBrussee/caveman · error
cave_delegate_depth_exceeded
cave_delegate_depth_exceeded
Error message
cave_delegate_depth_exceeded
What it means
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).
Source
Thrown at agents/delegate/caveman-delegate-mcp.mjs:141
},
required: ["task"],
},
};
async function handle(msg) {
const { id, method, params } = msg;
if (method === "initialize") {
return {
protocolVersion: params?.protocolVersion || PROTOCOL_FALLBACK,
capabilities: { tools: { listChanged: false } },
serverInfo: { name: "caveman-delegate", version: "0.1.0" },
};
}
if (method === "tools/list") return { tools: [TOOL] };
if (method === "ping") return {};
if (method === "tools/call") {
if (params?.name !== TOOL.name) throw new Error(`unknown tool: ${params?.name}`);
if (process.env.CAVEMAN_DELEGATE_CHILD === "1") throw new Error("cave_delegate_depth_exceeded");
const task = params?.arguments?.task;
if (!task || typeof task !== "string") throw new Error("cave_delegate_missing_task");
const r = await runWorker(task, params?.arguments?.cwd);
const lines = [r.out || "(worker produced no output)"];
if (r.usage) {
const u = r.usage;
let usageLine = `delegate usage (measured, provider-reported): input ${u.input + u.cacheRead + u.cacheWrite} (cacheRead ${u.cacheRead}, cacheWrite ${u.cacheWrite}), output ${u.output}`;
if (u.cost > 0) usageLine += `, cost $${u.cost.toFixed(4)}`;
lines.push("---", usageLine);
}
if (r.code !== 0) {
lines.push("---", `worker exit ${r.code}: ${r.err.slice(-500)}`);
return { content: [{ type: "text", text: lines.join("\n") }], isError: true };
}
return { content: [{ type: "text", text: lines.join("\n") }] };
}
if (method?.startsWith("notifications/")) return undefined;
throw Object.assign(new Error(`method not found: ${method}`), { code: -32601 });View on GitHub (pinned to 766dce6b13)
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.
Example fix
# before: nested delegation (inside a delegate worker)
server.callTool('caveman_delegate', { task: 'now do part 2' }) // throws
# after: orchestrator does both calls itself
await server.callTool('caveman_delegate', { task: 'part 1' })
await server.callTool('caveman_delegate', { task: 'part 2' }) Defensive patterns
Strategy: validation
Validate before calling
// Before invoking the delegate, check the depth guard's marker.
if (process.env.CAVEMAN_DELEGATE_CHILD === '1') {
throw new Error('already inside a delegate worker; nesting caveman_delegate is not allowed');
}
await client.request({
method: 'tools/call',
params: { name: 'caveman_delegate', arguments: { task: '...' } },
}); Try / catch
try {
await callCavemanDelegate(task);
} catch (e) {
if (String(e?.message).includes('cave_delegate_depth_exceeded')) {
// Restructure: return this subtask's result to the top-level orchestrator
// and issue the next caveman_delegate call from there.
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- filter monitor.id is required
- filter monitor.verdict must be pass, fail, or error
- ${key} must be one of ${values.join(", ")}
- ${key} must be a string
- page_size must be an integer from 1 to 500
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/b5ece32a4b43ceae.
Report an issue: GitHub.