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

  1. Pass recallBudget as an explicit non-negative integer literal or a value validated with Number.isSafeInteger(budget) && budget >= 0
  2. If the value comes from an env var or input string, coerce with Number(value) and reject NaN before calling memory()
  3. If a fractional budget is intentional, round it explicitly with Math.floor/Math.round and re-check >= 0
  4. 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

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


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)