JuliusBrussee/caveman · error · Error
caveman agent: subagent maxCalls must be a positive integer
Error message
caveman agent: subagent maxCalls must be a positive integer
What it means
Thrown by createSubagent (the tool factory that wraps an agent definition as a callable tool) when the maxCalls option is not a safe positive integer. maxCaps caps how many times the wrapped subagent may be invoked per tool call, so zero or fractional values are meaningless. The check runs at tool-definition time, before any agent execution starts.
Source
Thrown at packages/agent/src/index.ts:175
*/
maxCostUsd?: number;
/**
* This child's wallet in tokens — the denomination sibling of `maxCostUsd`,
* used by a token-metered run. A token-metered run cannot fund a subagent
* that declares no token wallet.
*/
maxTokens?: number;
maxContextTokens?: number;
}): ToolDefinition {
const maxInputChars = options.maxInputChars ?? 32_768;
if (!Number.isSafeInteger(maxInputChars) || maxInputChars <= 0) {
throw new Error("caveman agent: subagent maxInputChars must be a positive integer");
}
const maxCalls = options.maxCalls ?? 1;
const maxCostUsd = options.maxCostUsd ?? 1;
const maxContextTokens = options.maxContextTokens ?? 128_000;
if (!Number.isSafeInteger(maxCalls) || maxCalls <= 0) {
throw new Error("caveman agent: subagent maxCalls must be a positive integer");
}
if (!Number.isFinite(maxCostUsd) || maxCostUsd <= 0) {
throw new Error("caveman agent: subagent maxCostUsd must be positive");
}
if (options.maxTokens !== undefined &&
(!Number.isSafeInteger(options.maxTokens) || options.maxTokens <= 0)) {
throw new Error("caveman agent: subagent maxTokens must be a positive integer");
}
if (!Number.isSafeInteger(maxContextTokens) || maxContextTokens <= 0) {
throw new Error("caveman agent: subagent maxContextTokens must be a positive integer");
}
return tool({
name: options.name,
description: options.description,
input: schema.object({ task: schema.string() }),
effect: "read",
result: "auto",
...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),View on GitHub (pinned to 27d5a3981a)
Solutions
- Pass an explicit safe integer >= 1, e.g. maxCalls: 3
- If the value comes from config/env, coerce and validate first: const maxCalls = Number(raw); Number.isSafeInteger(maxCalls) && maxCalls > 0 ? maxCalls : 1
- Round budget-derived floats explicitly: Math.max(1, Math.floor(computed))
- Leave maxCalls undefined to accept the default of 1
Example fix
// before
const tool = subagentTool({ agent, name: "worker", maxCalls: Number(cfg.maxCalls) }); // cfg.maxCalls = "5" or 0
// after
const raw = Number(cfg.maxCalls);
const tool = subagentTool({
agent,
name: "worker",
maxCalls: Number.isSafeInteger(raw) && raw > 0 ? raw : 1,
}); Defensive patterns
Strategy: validation
Validate before calling
const maxCalls = Number(cfg.maxCalls);
if (!Number.isSafeInteger(maxCalls) || maxCalls <= 0) {
throw new Error(`config maxCalls must be a positive integer, got ${JSON.stringify(cfg.maxCalls)}`);
}
const t = subagentTool({ agent, name: "worker", maxCalls }); Type guard
const isPositiveInt = (v: unknown): v is number => Number.isSafeInteger(v) && (v as number) > 0;
Try / catch
try {
subagentTool({ agent, name: "worker", maxCalls });
} catch (e) {
if (e instanceof Error && e.message.includes("maxCalls")) { /* fix config, fail startup */ }
throw e;
} Prevention
- Treat subagent options as a validated config object parsed once at startup, not values threaded ad hoc
- Never pass raw env/config strings into numeric options without Number() plus Number.isSafeInteger checks
- Wrap tool-factory construction in a unit test that builds every tool from sample config files
When it happens
Trigger: Calling the subagent tool factory with options.maxCalls = 0, a negative number, a non-integer like 1.5, NaN, Infinity, or a numeric string such as "3". Note the default is 1; the error only fires when you pass an explicit invalid value.
Common situations: Computing maxCalls from an env var or config file (strings like process.env.MAX_CALLS arrive as "5"), passing a budget-derived float (e.g. cost/price = 2.5), or copying an example that used 0 to mean 'unlimited' (this library has no unlimited mode).
Related errors
- cave_breaker_threshold_invalid
- caveman agent: subagent maxInputChars must be a positive int
- caveman agent: subagent maxCostUsd must be positive
- caveman agent: subagent maxTokens must be a positive integer
- caveman agent: subagent maxContextTokens must be a positive
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/67949c5796c90908.
Report an issue: GitHub.