{"record":{"id":"c22d8e6b4b56dd0c","repo":"JuliusBrussee/caveman","slug":"cave-subagent-depth-limit-invalid","errorCode":"cave_subagent_depth_limit_invalid","errorMessage":"cave_subagent_depth_limit_invalid","messagePattern":"cave_subagent_depth_limit_invalid","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/runtime.ts","lineNumber":1090,"sourceCode":"  // Budget shape is settled before anything else happens: an ambiguous or\n  // unbounded budget must fail at run() start, not after the first dollar.\n  // maxCostUsd and budget are two different contracts for the same money —\n  // one terminates with an error, the other returns a planned partial result —\n  // so carrying both would leave the run's own stop semantics undecided.\n  if (options.budget !== undefined && options.maxCostUsd !== undefined) {\n    throw new Error(\"cave_budget_conflicting_cap\");\n  }\n  const budgetMeter = executionContext.budgetMeter ?? (options.budget === undefined\n    ? undefined\n    : new BudgetMeter(normalizeRunBudget(options.budget)));\n  if (options.deadlineMs !== undefined &&\n      (!Number.isSafeInteger(options.deadlineMs) || options.deadlineMs <= 0)) {\n    throw new Error(\"cave_run_deadline_invalid\");\n  }\n  if (options.maxSubagentDepth !== undefined &&\n      (!Number.isSafeInteger(options.maxSubagentDepth) || options.maxSubagentDepth <= 0 ||\n        options.maxSubagentDepth > ABSOLUTE_SUBAGENT_DEPTH_LIMIT)) {\n    throw new Error(\"cave_subagent_depth_limit_invalid\");\n  }\n  const deadlineAt = executionContext.deadlineAt ?? (options.deadlineMs === undefined\n    ? undefined\n    : performance.now() + options.deadlineMs);\n  // A controller with nothing to release would be a silent no-op at the\n  // checkpoint that expected it to matter.\n  if (options.budgetController !== undefined && budgetMeter === undefined) {\n    throw new Error(\"cave_budget_controller_without_budget\");\n  }\n  const breakers = options.breakers === undefined\n    ? undefined\n    : new BreakerState(normalizeRunBreakers(options.breakers, budgetMeter !== undefined));\n  const efficiencyPlan = options.lockedBuild?.selected_plan ?? options.candidatePlan;\n  const buildIdentity = options.lockedBuild === undefined\n    ? undefined\n    : {\n      buildSha256: options.lockedBuild.build_sha256,\n      planSha256: options.lockedBuild.plan_sha256,","sourceCodeStart":1072,"sourceCodeEnd":1108,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/runtime.ts#L1072-L1108","documentation":"RunOptions.maxSubagentDepth bounds how deep nested subagent invocation may go (default 2). streamAgentInternal validates it: a safe integer > 0 and at most ABSOLUTE_SUBAGENT_DEPTH_LIMIT. Values outside that range throw at run start before any spend.","triggerScenarios":"Passing maxSubagentDepth as 0, negative, non-integer, NaN/Infinity, or a value exceeding the absolute depth limit (greater than ABSOLUTE_SUBAGENT_DEPTH_LIMIT).","commonSituations":"Config-driven depth from env vars parsed loosely; trying to express \"unlimited depth\" with 0 or a huge number instead of omitting the option; arithmetic producing fractional values.","solutions":["Pass a positive safe integer within the absolute depth cap (e.g. 1–3).","Omit the option to use the default depth of 2.","Clamp untrusted input: Math.min(ABSOLUTE_LIMIT, Math.max(1, Math.floor(v)))."],"exampleFix":"// before\nrunAgent(def, input, { maxSubagentDepth: 0 });\n// after\nrunAgent(def, input, { maxSubagentDepth: 3 });","handlingStrategy":"validation","validationCode":"function isValidSubagentDepth(v) {\n  return v === undefined || (Number.isSafeInteger(v) && v > 0);\n}","typeGuard":"function hasValidMaxSubagentDepth(o) {\n  return o.maxSubagentDepth === undefined ||\n    (Number.isSafeInteger(o.maxSubagentDepth) && o.maxSubagentDepth > 0);\n}","tryCatchPattern":"try {\n  await runAgent(def, input, opts);\n} catch (err) {\n  if (err.message === \"cave_subagent_depth_limit_invalid\") {\n    await runAgent(def, input, { ...opts, maxSubagentDepth: 2 }); // library default\n  } else throw err;\n}","preventionTips":["Rely on the default depth of 2 unless you specifically need deeper nesting.","Clamp user/config input into [1, absolute limit] before assignment.","Never encode \"unlimited\" as 0 or a sentinel huge number."],"tags":["agent-runtime","validation","options","subagents"],"backgroundTag":"value-out-of-range","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"}