JuliusBrussee/caveman · error
caveman agent: subagent maxCalls must be a positive integer
Error message
caveman agent: subagent maxCalls must be a positive integer
What it means
The caveman agent `subagent()` helper validates its options before building the tool. `maxCalls` defaults to 1 and must be a positive safe integer (Number.isSafeInteger and > 0), since it caps how many times the subagent tool may be invoked. The library throws this error eagerly at tool-creation time when the caller passes a bad value such as 0, a negative number, a float, or a value exceeding the safe-integer range.
Solutions
- Pass a positive integer, e.g. `subagent({ maxCalls: 3 })`, or omit maxCalls to use the default of 1.
- If the value comes from config/env, coerce and validate first: `const maxCalls = Number.parseInt(raw, 10)` then check `Number.isSafeInteger(maxCalls) && maxCalls > 0`.
- To effectively disable the subagent, do not register it rather than setting maxCalls to 0.
Example fix
// before
const tool = subagent({ name: "researcher", maxCalls: Number(cfg.subagentCalls) });
// after
const maxCalls = Number.parseInt(cfg.subagentCalls, 10);
if (!Number.isSafeInteger(maxCalls) || maxCalls <= 0) throw new Error("bad subagentCalls config");
const tool = subagent({ name: "researcher", maxCalls }); Defensive patterns
Strategy: validation
Validate before calling
function assertMaxCalls(v) {
if (v === undefined) return 1;
if (!Number.isSafeInteger(v) || v <= 0) throw new TypeError(`maxCalls must be a positive integer, got ${v}`);
return v;
}
subagent({ name: "researcher", maxCalls: assertMaxCalls(cfg.maxCalls) }); Type guard
const isPosInt = (v) => typeof v === "number" && Number.isSafeInteger(v) && v > 0;
Try / catch
try {
const t = subagent({ maxCalls: cfg.maxCalls });
} catch (e) {
if (e.message.includes("maxCalls must be a positive integer")) {
console.error(`Config error: subagentCalls='${cfg.maxCalls}' is not a positive integer`);
process.exitCode = 2;
} else throw e;
} Prevention
- Validate numeric config at load time with a schema library (zod/valibot) using positive-int checks
- Never pass raw env-var strings as numeric options; parse and check first
- Prefer omitting an option over sentinel values like 0 or Infinity to disable behavior
- Add a unit test that constructs every tool with your production config
When it happens
Trigger: Calling `subagent({ maxCalls: 0 })`, `subagent({ maxCalls: -1 })`, `subagent({ maxCalls: 2.5 })`, `subagent({ maxCalls: NaN })`, `subagent({ maxCalls: Infinity })`, or a string coerced from user config instead of a number.
Common situations: Loading maxCalls from an environment variable, CLI flag, or JSON config as a string without parsing; computing maxCalls with arithmetic that yields NaN; copying an example with `maxCalls: 0` to 'disable' the subagent instead of omitting the tool entirely.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- caveman agent: subagent maxContextTokens must be a positive…
- caveman agent: subagent maxCostUsd must be positive
- caveman agent: subagent maxTokens must be a positive integer
- apiKey, baseURL, and agent are required
- ASGI context resolver or request bounds are invalid
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/67949c5796c90908.
Report an issue: GitHub.
Appendix: 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 3ee70a1026)