{"record":{"id":"8bbd7a76df744dc4","repo":"JuliusBrussee/caveman","slug":"cave-subagent-runtime-missing","errorCode":"cave_subagent_runtime_missing","errorMessage":"cave_subagent_runtime_missing","messagePattern":"cave_subagent_runtime_missing","errorType":"error_code","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/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/runtime.ts#L3848-L3884","documentation":"The runtime looks up toolDefinition.runtime and requires it to be a subagent-kind runtime descriptor before executing a subagent tool call. If runtime is missing entirely or has a kind other than \"subagent\", the tool definition is not wired as a subagent and execution cannot proceed, so this error is thrown. It indicates a tool-registration/definition defect rather than bad user input.","triggerScenarios":"Invoking a tool whose ToolDefinition was created without a runtime field, or whose runtime.kind is not \"subagent\", through the subagent execution path.","commonSituations":"Hand-built tool definitions missing the runtime descriptor; a plugin/extension passing a normal tool into the subagent runner; schema/refactor renaming the runtime field; version mismatch where tool definitions created by an older factory lack runtime.","solutions":["Ensure the tool definition is created via the library's subagent tool factory so runtime.kind === \"subagent\" is set","Inspect toolDefinition.runtime before dispatch; only route tools with a subagent runtime into this path","Upgrade/align library versions if tool definitions come from a plugin built against a different schema","Verify the tool name resolves to the intended subagent definition, not a regular tool"],"exampleFix":"// before\ndefineTool({ name: \"researcher\", execute });\n// after\ndefineTool({ name: \"researcher\", runtime: { kind: \"subagent\", maxInputChars: 100000, maxCalls: 5, maxTokens: 50000 }, execute });","handlingStrategy":"type-guard","validationCode":"if (toolDefinition.runtime?.kind !== \"subagent\") {\n  throw new Error(toolDefinition.name + \" is not registered as a subagent tool\");\n}","typeGuard":"function isSubagentTool(t) {\n  return t.runtime?.kind === \"subagent\";\n}","tryCatchPattern":"try {\n  await dispatchSubagent({ task });\n} catch (e) {\n  if (e.message === \"cave_subagent_runtime_missing\") {\n    throw new Error(\"Tool is not wired as a subagent; check its ToolDefinition\");\n  } else throw e;\n}","preventionTips":["Always build subagent tools with the library's factory helpers","Assert runtime.kind === \"subagent\" at tool registration time in tests","Pin plugin/SDK versions so definition schemas match","Never hand-roll ToolDefinition objects for subagents"],"tags":["subagent","runtime","configuration"],"backgroundTag":"missing-required-config-field","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}