JuliusBrussee/caveman · error
cave_subagent_invocation_limit_invalid
cave_subagent_invocation_limit_invalid
Error message
cave_subagent_invocation_limit_invalid
What it means
Thrown at the start of streamAgentInternal when RunOptions.maxSubagentInvocations is present but invalid: it must be a safe integer, strictly positive, and at most ABSOLUTE_SUBAGENT_INVOCATION_LIMIT (1,000,000). This option bounds monotonic subagent admissions across all tools and depths on one root ledger, so a fractional, zero, negative, or oversized value is rejected before the run does any work.
Source
Thrown at packages/agent/src/runtime.ts:1062
controller.abort(error);
return inner.throw(error);
},
[Symbol.asyncIterator]() {
return this;
},
} as AsyncGenerator<CavemanRunEvent>;
}
async function* streamAgentInternal(
definition: AgentDefinition,
input: string,
options: InternalRunOptions,
executionContext: InternalExecutionContext,
): AsyncGenerator<CavemanRunEvent> {
if (options.maxSubagentInvocations !== undefined &&
(!Number.isSafeInteger(options.maxSubagentInvocations) || options.maxSubagentInvocations <= 0 ||
options.maxSubagentInvocations > ABSOLUTE_SUBAGENT_INVOCATION_LIMIT)) {
throw new Error("cave_subagent_invocation_limit_invalid");
}
if (options.maxConcurrentSubagents !== undefined &&
(!Number.isSafeInteger(options.maxConcurrentSubagents) || options.maxConcurrentSubagents <= 0 ||
options.maxConcurrentSubagents > ABSOLUTE_SUBAGENT_INVOCATION_LIMIT)) {
throw new Error("cave_subagent_concurrency_limit_invalid");
}
if (options.lockedBuild !== undefined && options.candidatePlan !== undefined) {
throw new Error("cave_execution_authorization_ambiguous");
}
// Budget shape is settled before anything else happens: an ambiguous or
// unbounded budget must fail at run() start, not after the first dollar.
// maxCostUsd and budget are two different contracts for the same money —
// one terminates with an error, the other returns a planned partial result —
// so carrying both would leave the run's own stop semantics undecided.
if (options.budget !== undefined && options.maxCostUsd !== undefined) {
throw new Error("cave_budget_conflicting_cap");
}
const budgetMeter = executionContext.budgetMeter ?? (options.budget === undefinedView on GitHub (pinned to 27d5a3981a)
Solutions
- Pass a positive safe integer within the limit, e.g. maxSubagentInvocations: 8, or omit it to keep prior unbounded behavior
- To express 'no subagents', remove subagent tools from the definition instead of passing 0
- Clamp computed values: Math.max(1, Math.min(1_000_000, Math.floor(configValue)))
Example fix
// before
await agent.run(input, { maxSubagentInvocations: limit }); // limit = 0 from unset config
// after
const limit = Number(process.env.MAX_SUBS);
await agent.run(input, {
...(Number.isSafeInteger(limit) && limit > 0
? { maxSubagentInvocations: Math.min(limit, 1_000_000) }
: {}),
}); Defensive patterns
Strategy: validation
Validate before calling
function clampLimit(v: unknown, max = 1_000_000): number | undefined {
return typeof v === "number" && Number.isSafeInteger(v) && v > 0 && v <= max
? v : undefined;
}
const opts = { maxSubagentInvocations: clampLimit(config.maxSubs) }; Type guard
const isValidInvocationLimit = (v: unknown): v is number => typeof v === "number" && Number.isSafeInteger(v) && v > 0 && v <= 1_000_000;
Prevention
- Express 'no subagents' by removing subagent tools, never via 0
- Clamp config-derived integers with Math.floor and bounds
- Keep limits in one typed config object validated at startup
When it happens
Trigger: Setting maxSubagentInvocations to 0 (attempting to 'disable' subagents), a non-integer (1.5), a value above 1,000,000, or a NaN/computed value from config.
Common situations: Deriving the limit from a config or env value that defaults to 0 when unset; dividing a budget into counts and passing a fractional result; trying to express 'no subagents allowed' with 0.
Related errors
- cave_subagent_depth_limit_invalid
- caveman agent: run maxCostUsd must be positive
- cave_subagent_concurrency_limit_invalid
- cave_run_deadline_invalid
- cave_budget_controller_without_budget
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/67a084271e04f100.
Report an issue: GitHub.