JuliusBrussee/caveman · error · Error
caveman agent: tool timeoutMs must be a positive integer
Error message
caveman agent: tool timeoutMs must be a positive integer
What it means
tool() validates the per-call timeout: timeoutMs must be a safe positive integer (default 30_000 ms). Fractional timeouts, zero, negatives, NaN, or numeric strings are rejected because the runtime schedules calls with this exact value. Throws synchronously at definition time.
Source
Thrown at packages/agent/src/primitives.ts:142
options: ToolOptions<TSchema, unknown> |
StandardToolOptions<unknown, unknown, unknown> |
StandardJSONToolOptions<unknown, unknown, unknown>,
): ToolDefinition<unknown, unknown> {
if (!/^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/.test(options.name)) {
throw new Error(`caveman agent: invalid tool name ${JSON.stringify(options.name)}`);
}
if (!["read", "write", "idempotent", "external"].includes(options.effect)) {
throw new Error(`caveman agent: unknown tool effect ${JSON.stringify(options.effect)}`);
}
const result = typeof options.result === "object"
? artifactResultPolicy(options.result)
: options.result ?? "auto";
if (!["auto", "inline", "page", "compress", "exact_ccr"].includes(result)) {
throw new Error(`caveman agent: unknown tool result policy ${JSON.stringify(result)}`);
}
const timeoutMs = options.timeoutMs ?? 30_000;
if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) {
throw new Error("caveman agent: tool timeoutMs must be a positive integer");
}
const standard = standardToolSchema(options.input);
let input: TSchema;
if (standard === undefined) {
input = options.input as TSchema;
} else {
let converted = "inputJSONSchema" in options
? options.inputJSONSchema
: undefined;
if (converted === undefined && standard.jsonSchema !== undefined) {
try {
converted = standard.jsonSchema.input({ target: "draft-07" });
} catch (error) {
throw new Error("caveman agent: Standard Schema cannot emit draft-07 input JSON Schema", {
cause: error,
});
}
}View on GitHub (pinned to 27d5a3981a)
Solutions
- Pass a positive integer of milliseconds, e.g. timeoutMs: 60_000
- Omit timeoutMs to accept the 30 s default
- Convert and round user input: Math.max(1, Math.round(Number(raw)))
- There is no 'no timeout' value — pick the largest ceiling you accept
Example fix
// before
tool({ name: "run", effect: "external", timeoutMs: Number(cfg.timeout), execute: ... }); // cfg.timeout = "30s" -> NaN
// after
const secs = parseFloat(cfg.timeout);
tool({
name: "run",
effect: "external",
timeoutMs: Number.isFinite(secs) ? Math.max(1, Math.round(secs * 1000)) : 30_000,
execute: ...,
}); Defensive patterns
Strategy: validation
Validate before calling
const t = Number(cfg.timeoutMs);
if (!Number.isSafeInteger(t) || t <= 0) {
throw new Error(`timeoutMs must be positive integer milliseconds, got ${JSON.stringify(cfg.timeoutMs)}`);
} Type guard
const isPositiveInt = (v: unknown): v is number => Number.isSafeInteger(v) && (v as number) > 0;
Prevention
- Always name timeout config in milliseconds explicitly (timeoutMs, never 'timeout' in seconds)
- Round parsed values: Math.max(1, Math.round(Number(raw)))
- Reject -1/0 sentinels at your config boundary — this library has no infinite timeout
When it happens
Trigger: Passing timeoutMs: 0 (often meant as 'no timeout'), timeoutMs: 1.5, timeoutMs: "30000", or timeoutMs: -1. Computed values like seconds-to-ms math gone wrong (0.5 * 1000 with truncation elsewhere) also land here.
Common situations: Config-driven timeouts parsed from YAML/CLI as strings, milliseconds/seconds confusion (passing 30 meaning 30 ms when 30 s was intended — valid but a footgun — or 0.03 * 1000 = 30.000000000000004 float), or copying a default of -1 from another library meaning 'infinite'.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- caveman agent: file path is required
- caveman agent: invalid tool name ${JSON.stringify(options.na
- caveman agent: unknown tool effect ${JSON.stringify(options.
- caveman agent: unknown tool result policy ${JSON.stringify(r
- caveman agent: Standard Schema emitted invalid input JSON Sc
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/3f06669c4680dbf1.
Report an issue: GitHub.