JuliusBrussee/caveman · error
cave_subagent_concurrency_limit_invalid
cave_subagent_concurrency_limit_invalid
Error message
cave_subagent_concurrency_limit_invalid
What it means
Thrown at the start of streamAgentInternal when RunOptions.maxConcurrentSubagents 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 simultaneously active descendants on the root ledger; capacity is released after success, error, or abort, so a malformed cap is rejected up front rather than silently ignored.
Source
Thrown at packages/agent/src/runtime.ts:1067
},
} 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 === undefined
? undefined
: new BudgetMeter(normalizeRunBudget(options.budget)));
if (options.deadlineMs !== undefined &&
(!Number.isSafeInteger(options.deadlineMs) || options.deadlineMs <= 0)) {
throw new Error("cave_run_deadline_invalid");View on GitHub (pinned to 27d5a3981a)
Solutions
- Pass a positive safe integer, e.g. maxConcurrentSubagents: 4, or omit it for no concurrency bound
- For 'serial' subagent execution use 1, not 0
- Sanitize computed values: Math.max(1, Math.floor(n))
Example fix
// before
await agent.run(input, { maxConcurrentSubagents: cpus - 4 }); // 0 on a 4-core box
// after
await agent.run(input, {
maxConcurrentSubagents: Math.max(1, cpus - 4),
}); Defensive patterns
Strategy: validation
Validate before calling
const concurrency = Math.max(1, Math.floor(config.concurrency ?? 1));
await agent.run(input, { maxConcurrentSubagents: concurrency }); Type guard
const isValidConcurrency = (v: unknown): v is number => typeof v === "number" && Number.isSafeInteger(v) && v > 0 && v <= 1_000_000;
Prevention
- Use 1 for serial subagent execution, omit for unbounded
- Guard computed concurrency (cpu counts, config) against 0
- Never pass fractional concurrency values
When it happens
Trigger: Setting maxConcurrentSubagents to 0, a negative number, a non-integer like 2.5, NaN, or a value above 1,000,000.
Common situations: Computing concurrency from CPU count or config and passing 0 on small machines; passing a parsed env value that is empty (NaN); mixing up 'no concurrency limit' (should be omitted) with 0.
Related errors
- cave_subagent_invocation_limit_invalid
- cave_subagent_depth_limit_invalid
- option not found
- cave_harness_adapter_version_invalid
- cave_harness_model_invalid
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/072ed9e10e3ec079.
Report an issue: GitHub.