{"record":{"id":"8bbd7a76df744dc4","repo":"JuliusBrussee/caveman","slug":"cave-subagent-runtime-missing","errorCode":null,"errorMessage":"cave_subagent_runtime_missing","messagePattern":"cave_subagent_runtime_missing","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/runtime.ts","lineNumber":3866,"sourceCode":"  return Object.freeze({\n    traceId: parent.traceId,\n    spanId: randomBytes(8).toString(\"hex\"),\n    parentSpanId: parent.spanId,\n  });\n}\n\nasync function executeSubagent(\n  toolDefinition: ToolDefinition,\n  params: unknown,\n  signal: AbortSignal | undefined,\n  parentOptions: InternalRunOptions,\n  usage: NestedUsage,\n  executionContext: InternalExecutionContext,\n  parentMeter: BudgetMeter | undefined,\n  parentDeadlineAt: number | undefined,\n): Promise<unknown> {\n  const runtime = toolDefinition.runtime;\n  if (runtime?.kind !== \"subagent\") throw new Error(\"cave_subagent_runtime_missing\");\n  if (params === null || typeof params !== \"object\" || Array.isArray(params) ||\n      typeof (params as { task?: unknown }).task !== \"string\") {\n    throw new Error(\"cave_subagent_arguments_invalid\");\n  }\n  const task = (params as { task: string }).task;\n  if (task.length > runtime.maxInputChars) throw new Error(\"cave_subagent_input_limit\");\n  const calls = usage.calls.get(toolDefinition.name) ?? 0;\n  if (calls >= runtime.maxCalls) throw new Error(\"cave_subagent_call_budget\");\n  // Reserve synchronously before any await so parallel Pi tool dispatch cannot\n  // pass the same maxCalls check twice.\n  usage.calls.set(toolDefinition.name, calls + 1);\n  const depth = executionContext.depth;\n  const depthLimit = Math.min(\n    parentOptions.maxSubagentDepth ?? DEFAULT_SUBAGENT_DEPTH_LIMIT,\n    ABSOLUTE_SUBAGENT_DEPTH_LIMIT,\n  );\n  if (depth + 1 > depthLimit) throw new Error(\"cave_subagent_depth_limit\");\n  // The wallet is carved here, still synchronously, for the same reason: two","sourceCodeStart":3848,"sourceCodeEnd":3884,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/runtime.ts#L3848-L3884","documentation":"executeSubagent throws this when the tool definition it was handed does not carry runtime.kind === \"subagent\". It is an internal dispatch invariant: the subagent execution path may only run against tools declared with a subagent runtime. Hitting it as a user means the definition graph or tool wiring routed a normal tool into the nested-agent executor.","triggerScenarios":"A tool definition reaches executeSubagent without a runtime object, or with runtime.kind other than \"subagent\" (e.g. hand-built ToolDefinition objects passed where subagent() was intended, or a graph mutation stripped the runtime field).","commonSituations":"Building tool definitions manually instead of via the subagent() builder; spreading/copying a tool definition and dropping the runtime property; version drift between the builder API and runtime dispatcher.","solutions":["Create nested-agent tools with the framework's subagent() builder rather than hand-authoring ToolDefinition objects.","Verify toolDefinition.runtime?.kind === \"subagent\" for every tool you route into nested execution before dispatch.","Check for definition copies/spreads that omit the runtime field (it is non-enumerable or stripped in some clone paths)."],"exampleFix":"// before\nconst tool: ToolDefinition = { name: \"helper\", description: \"...\", execute: nestedRun };\n\n// after\nconst tool = subagent({ name: \"helper\", description: \"...\", /* agent definition */ });","handlingStrategy":"type-guard","validationCode":"if (tool.runtime?.kind !== \"subagent\") {\n  throw new Error(`tool ${tool.name} lacks subagent runtime; build it with subagent()`);\n}","typeGuard":"const hasSubagentRuntime = (t: ToolDefinition): boolean =>\n  t.runtime?.kind === \"subagent\";","tryCatchPattern":"try {\n  await executeNested(tool);\n} catch (error) {\n  if (error instanceof Error && error.message === \"cave_subagent_runtime_missing\") {\n    // definition wiring bug — rebuild the tool with subagent(), do not retry\n  } else throw error;\n}","preventionTips":["Always construct nested-agent tools with the subagent() builder; never hand-write runtime objects.","Assert runtime.kind on every tool routed into nested execution in tests.","Avoid deep-cloning tool definitions; clone paths can drop the runtime field."],"tags":["subagent","tool-definition","internal-invariant"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}