JuliusBrussee/caveman · error
caveman agent: memory recallBudget must be a non-negative…
Error message
caveman agent: memory recallBudget must be a non-negative integer
What it means
The memory() primitive validates that options.recallBudget is a safe, non-negative integer before constructing a MemoryDefinition. recallBudget caps how many memory records a recall call may return, so a fractional, negative, non-numeric, or unsafe (> Number.MAX_SAFE_INTEGER) value is rejected at construction time. Throwing early keeps a malformed budget from silently breaking recall behavior later at tool-call time.
Solutions
- Pass recallBudget as an explicit non-negative integer literal or a value validated with Number.isSafeInteger(budget) && budget >= 0
- If the value comes from an env var or input string, coerce with Number(value) and reject NaN before calling memory()
- If a fractional budget is intentional, round it explicitly with Math.floor/Math.round and re-check >= 0
- If the budget is optional, omit the field or pass a defined default such as 0 rather than undefined/null
Example fix
// before
const budget = process.env.MEMORY_RECALL_BUDGET; // "20" (string)
defineMemory({ namespace: "notes", recallBudget: budget });
// after
const budget = Number(process.env.MEMORY_RECALL_BUDGET);
if (!Number.isSafeInteger(budget) || budget < 0) {
throw new Error(`MEMORY_RECALL_BUDGET must be a non-negative integer, got ${process.env.MEMORY_RECALL_BUDGET}`);
}
defineMemory({ namespace: "notes", recallBudget: budget }); Defensive patterns
Strategy: validation
Validate before calling
function isValidRecallBudget(v) {
return typeof v === "number" && Number.isSafeInteger(v) && v >= 0;
}
// before calling: if (!isValidRecallBudget(budget)) throw new Error(`bad recallBudget: ${budget}`); Type guard
function isRecallBudget(v: unknown): v is number {
return typeof v === "number" && Number.isSafeInteger(v) && v >= 0;
} Try / catch
try {
const def = memory({ namespace: "notes", recallBudget: budget });
} catch (err) {
if (err instanceof Error && err.message.includes("recallBudget must be a non-negative integer")) {
console.error(`Invalid recallBudget ${JSON.stringify(budget)}: use a non-negative integer`);
return;
}
throw err;
} Prevention
- Coerce env/CLI values with Number() and validate with Number.isSafeInteger before use
- Never compute budgets as fractional ratios without rounding
- Centralize memory options construction in one validated helper
- Add a unit test asserting memory() rejects -1, 2.5, NaN, and "10"
When it happens
Trigger: Calling memory() with recallBudget set to a negative number (e.g. -1), a float (e.g. 2.5), a non-number (e.g. a numeric string "10" or undefined/null reaching this path), or a value beyond Number.MAX_SAFE_INTEGER. Number.isSafeInteger also rejects NaN and Infinity.
Common situations: Reading the budget from an env var or CLI flag without parsing it to a number (it stays a string); computing the budget as a ratio or percentage (fractional result); a typo or swapped variable passing a different negative value; config deserialized from JSON where the field was null.
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
- cave_mastra_max_steps_invalid
- caveman agent: context id is required
- caveman agent: memory ttl must use positive m, h, or d…
- invalid_deadline
- maxLoadedTools must be a positive integer
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/062cd4aef838433f.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/primitives.ts:272
const milliseconds = amount * scale;
if (!Number.isSafeInteger(amount) || !Number.isSafeInteger(milliseconds) || milliseconds <= 0) {
throw new Error("cave_memory_ttl_invalid");
}
return milliseconds;
}
export function memory(options: {
namespace: string;
provenance?: MemoryDefinition["provenance"];
ttl: string;
recallBudget: number;
consent?: MemoryDefinition["consent"];
}): MemoryDefinition {
if (!/^[a-z0-9][a-z0-9_-]{0,95}$/.test(options.namespace)) {
throw new Error(`caveman agent: invalid memory namespace ${JSON.stringify(options.namespace)}`);
}
if (!Number.isSafeInteger(options.recallBudget) || options.recallBudget < 0) {
throw new Error("caveman agent: memory recallBudget must be a non-negative integer");
}
try {
memoryTTLMilliseconds(options.ttl);
} catch {
throw new Error("caveman agent: memory ttl must use positive m, h, or d duration");
}
// Fail closed at CONSTRUCTION, not at tool-call time: the durable
// store implements only local, single-tenant memory. A `project`/`external`
// provenance or a `project_shared` consent is a shared-backend contract this
// package does not provide, so it is refused here rather than burning a model
// turn to discover a config the framework already knew was unsupported.
const provenance = options.provenance ?? "local";
if (provenance !== "local") {
throw new Error("cave_memory_provenance_unsupported: only \"local\" memory is supported");
}
const consent = options.consent ?? "local_only";
if (consent !== "local_only") {
throw new Error("cave_memory_consent_unsupported: only \"local_only\" consent is supported");View on GitHub (pinned to 3ee70a1026)