{"record":{"id":"e210dad86df35eb9","repo":"JuliusBrussee/caveman","slug":"cave-subagent-context-budget","errorCode":"cave_subagent_context_budget","errorMessage":"cave_subagent_context_budget","messagePattern":"cave_subagent_context_budget","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/runtime.ts","lineNumber":3952,"sourceCode":"    toolDefinition,\n    runtime,\n    task,\n    signal,\n    parentOptions,\n    usage,\n    executionContext,\n    depth,\n  } = input;\n  if (signal?.aborted) throw signal.reason;\n  const childDefinition = runtime.definition as AgentDefinition;\n  const childContext = await lowerAgentContext(childDefinition, {\n    ...(parentOptions.rootDir === undefined ? {} : { rootDir: parentOptions.rootDir }),\n    input: task,\n  });\n  const childContextTokens = childContext.ir.segments\n    .reduce((total, segment) => total + segment.tokenCount, 0);\n  if (childContextTokens > runtime.maxContextTokens) {\n    throw new Error(\"cave_subagent_context_budget\");\n  }\n  const childUsesAuto = childDefinition.model !== null &&\n    typeof childDefinition.model === \"object\" &&\n    \"kind\" in childDefinition.model &&\n    childDefinition.model.kind === \"auto\";\n  const childModel = childUsesAuto && parentOptions.model !== undefined\n    ? parentOptions.model\n    : resolveModel(\n      childDefinition,\n      parentOptions.models ?? builtinModels(),\n      parentOptions.rootDir ?? process.cwd(),\n    );\n  // A carved wallet is already the child's hard economic boundary in the\n  // parent's denomination. Stacking the legacy USD-only ledger on a token\n  // wallet would require catalog pricing and reject otherwise valid raw-token\n  // runs (including subscription/unpriced transports). Keep that ledger only\n  // for standalone legacy subagents that have no carved BudgetMeter.\n  let spendLedger: SpendLedger | undefined;","sourceCodeStart":3934,"sourceCodeEnd":3970,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/packages/agent/src/runtime.ts#L3934-L3970","documentation":"Before a subagent runs, its agent definition is lowered to Context IR (system prompt plus the task text) and the token counts of those static segments are summed. If that sum already exceeds the child runtime's maxContextTokens, dispatch throws immediately: the child's own prompt+task cannot fit its declared context window, so any provider call would be wasted. This is the pre-flight (estimated) check, distinct from the post-run actual check at line 4057.","triggerScenarios":"A subagent definition with a large system prompt and a long task string while its runtime maxContextTokens is smaller than prompt+task; explicitly setting maxContextTokens low (e.g., 8k) on a verbose agent; passing a whole document as the task payload to a small-window child; child model resolution falling back to a small-context model.","commonSituations":"Copy-pasting a big prompt template into a subagent configured for a small-window model; mistaking the model's max output tokens for its context window; escalating task payloads (full file contents, logs) into a child without trimming; inheriting parent prompt scaffolding into the child definition.","solutions":["Raise the child's maxContextTokens above prompt+task with headroom for output and tool results","Shorten the task string passed to the subagent (summarize, chunk, or reference instead of inlining)","Shrink the child's system prompt or move boilerplate into a file the child reads via a tool","Point the child at a model with a larger context window"],"exampleFix":"// before: 4k window child receives a huge task\nconst researcher = parent.subagent({ model: smallCtxModel, maxContextTokens: 4096 });\nawait researcher(task: entireCodebaseDump);\n\n// after: trim input and raise the cap\nawait researcher({ task: 'Summarize the top 3 findings from these notes: ' + notes.slice(0, 2000) });\n// and set maxContextTokens to at least prompt+task+output headroom, e.g. 32_768","handlingStrategy":"validation","validationCode":"// Rough pre-flight: static prompt + task must fit the child window (chars/4 estimate)\nconst estimateTokens = (s: string) => Math.ceil(s.length / 4);\nconst childStatic = estimateTokens(childDefinition.systemPrompt ?? '') + estimateTokens(task);\nconst OUTPUT_HEADROOM = estimateTokens(childDefinition.systemPrompt ?? '') + 8_192; // conservative\nif (childStatic + OUTPUT_HEADROOM > childMaxContextTokens) {\n  task = task.slice(0, (childMaxContextTokens - OUTPUT_HEADROOM) * 2); // trim before dispatch\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await dispatchSubagent(task);\n} catch (error) {\n  if (error instanceof Error && error.message === 'cave_subagent_context_budget') {\n    throw new Error(`task too large for subagent window (${task.length} chars); trim or raise maxContextTokens`);\n  }\n  throw error;\n}","preventionTips":["Keep subagent system prompts small; push bulk content to files the child fetches via tools","Set maxContextTokens from the child model's real context window minus expected output, not from its max output tokens","Trim/summarize task payloads before passing them to subagents","Pin the child model instead of 'auto' so the window you sized for is the window that runs"],"tags":["subagent","context-window","tokens","pre-flight-check"],"backgroundTag":"context-window-exceeded","analyzedSha":"766dce6b1394ebb56a3090748d5a0240a5aefb36","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}