JuliusBrussee/caveman · error
initialToolCount must be a non-negative integer
Error message
initialToolCount must be a non-negative integer
What it means
The tools() descriptor validates initialToolCount (default 8): when provided it must be a safe integer >= 0. Negative numbers, non-integers, NaN/Infinity, or out-of-range values are rejected. This count decides how many tools are pre-loaded in the deferred strategy, so it must be a valid array-size-like number.
Solutions
- Pass a non-negative integer (e.g. initialToolCount: 4) or omit the option to use the default of 8.
- Clamp computed values: `initialToolCount: Math.max(0, Math.trunc(count))`.
- Coerce config values with Number() and validate Number.isSafeInteger(v) && v >= 0 before calling.
Example fix
// before
cave.tools({ catalog, initialToolCount: catalog.length - 10 }); // negative for small catalogs
// after
cave.tools({ catalog, initialToolCount: Math.max(0, catalog.length - 10) }); Defensive patterns
Strategy: validation
Validate before calling
function parseInitialToolCount(raw: unknown): number | undefined {
if (raw === undefined) return undefined;
const n = Number(raw);
if (!Number.isSafeInteger(n) || n < 0) throw new Error(`initialToolCount must be a non-negative integer, got ${String(raw)}`);
return n;
} Type guard
const isValidInitialToolCount = (v: unknown): v is number => typeof v === "number" && Number.isSafeInteger(v) && v >= 0;
Try / catch
try {
const desc = cave.tools({ catalog, initialToolCount });
} catch (e) {
if (e instanceof Error && e.message.includes("initialToolCount")) {
const desc = cave.tools({ catalog }); // default of 8
} else throw e;
} Prevention
- Clamp computed counts with Math.max(0, Math.trunc(v)) before passing.
- Omit initialToolCount to use the default (8).
- Validate config-sourced numbers with Number.isSafeInteger before use.
When it happens
Trigger: cave.tools({ catalog, initialToolCount: -1 }) (e.g. "auto" intent), initialToolCount: 2.5, initialToolCount: Number(config.initialToolCount) yielding NaN, or initialToolCount exceeding catalog length with an unsafe/huge value.
Common situations: Config parsing leaving the value as a string or producing NaN; computing the count from catalog.length - n going negative for tiny catalogs; seconds/millis or percentage confusion producing fractional values.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- ASGI context resolver or request bounds are invalid
- strategy must be all or deferred
- apiKey, baseURL, and agent are required
- AutoGen tools and workbench are mutually exclusive
- bedrock endpoint must be runtime or mantle
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/16a4131762622e46.
Report an issue: GitHub.
Appendix: source
Thrown at packages/sdk/typescript/src/index.ts:475
/**
* Build a tool-catalog handle with server-side search via /sdk/v1/tool-search.
*
* strategy "all" → initial = whole catalog; search() still calls the server.
* strategy "deferred" → initial = alwaysLoad tools (subset sent on first turn);
* search() calls the server to load relevant tools on demand.
*
* Breaking change from 1.0: search() is now async and returns ToolSearchResult
* (previously sync, returning CaveTool[]). Callers must await search().
*/
tools(config: { catalog: CaveTool[]; strategy?: "all" | "deferred"; initialToolCount?: number; maxLoadedTools?: number }) {
const strategy = config.strategy ?? "all";
if (strategy !== "all" && strategy !== "deferred") {
throw new Error("strategy must be all or deferred");
}
const initialToolCount = config.initialToolCount ?? 8;
if (!Number.isSafeInteger(initialToolCount) || initialToolCount < 0) {
throw new Error("initialToolCount must be a non-negative integer");
}
if (config.maxLoadedTools !== undefined && (!Number.isSafeInteger(config.maxLoadedTools) || config.maxLoadedTools < 1)) {
throw new Error("maxLoadedTools must be a positive integer");
}
let initial: CaveTool[];
if (strategy === "all") {
initial = config.catalog.slice();
} else {
const always = config.catalog.filter((tool) => tool.alwaysLoad);
if (config.maxLoadedTools !== undefined && always.length > config.maxLoadedTools) {
throw new Error("maxLoadedTools must be at least the alwaysLoad tool count");
}
const lazy = config.catalog.filter((tool) => !tool.alwaysLoad);
const lazyLimit = config.maxLoadedTools === undefined
? initialToolCount
: Math.min(initialToolCount, config.maxLoadedTools - always.length);
initial = always.concat(lazy.slice(0, lazyLimit));
}View on GitHub (pinned to 3ee70a1026)