{"record":{"id":"062cd4aef838433f","repo":"JuliusBrussee/caveman","slug":"caveman-agent-memory-recallbudget-must-be-a-non-n","errorCode":null,"errorMessage":"caveman agent: memory recallBudget must be a non-negative integer","messagePattern":"caveman agent: memory recallBudget must be a non-negative integer","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/primitives.ts","lineNumber":272,"sourceCode":"  const milliseconds = amount * scale;\n  if (!Number.isSafeInteger(amount) || !Number.isSafeInteger(milliseconds) || milliseconds <= 0) {\n    throw new Error(\"cave_memory_ttl_invalid\");\n  }\n  return milliseconds;\n}\n\nexport function memory(options: {\n  namespace: string;\n  provenance?: MemoryDefinition[\"provenance\"];\n  ttl: string;\n  recallBudget: number;\n  consent?: MemoryDefinition[\"consent\"];\n}): MemoryDefinition {\n  if (!/^[a-z0-9][a-z0-9_-]{0,95}$/.test(options.namespace)) {\n    throw new Error(`caveman agent: invalid memory namespace ${JSON.stringify(options.namespace)}`);\n  }\n  if (!Number.isSafeInteger(options.recallBudget) || options.recallBudget < 0) {\n    throw new Error(\"caveman agent: memory recallBudget must be a non-negative integer\");\n  }\n  try {\n    memoryTTLMilliseconds(options.ttl);\n  } catch {\n    throw new Error(\"caveman agent: memory ttl must use positive m, h, or d duration\");\n  }\n  // Fail closed at CONSTRUCTION, not at tool-call time: the durable\n  // store implements only local, single-tenant memory. A `project`/`external`\n  // provenance or a `project_shared` consent is a shared-backend contract this\n  // package does not provide, so it is refused here rather than burning a model\n  // turn to discover a config the framework already knew was unsupported.\n  const provenance = options.provenance ?? \"local\";\n  if (provenance !== \"local\") {\n    throw new Error(\"cave_memory_provenance_unsupported: only \\\"local\\\" memory is supported\");\n  }\n  const consent = options.consent ?? \"local_only\";\n  if (consent !== \"local_only\") {\n    throw new Error(\"cave_memory_consent_unsupported: only \\\"local_only\\\" consent is supported\");","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/primitives.ts#L254-L290","documentation":"Thrown by the memory() builder when options.recallBudget is not a safe non-negative integer. The recall budget bounds how many memories a recall pass may return/consume, so fractional values (0.5), negatives, NaN, Infinity, or numeric strings are all rejected because they cannot bound retrieval deterministically.","triggerScenarios":"Calling memory({ recallBudget: ... }) with 0.5, -1, NaN, Infinity, a value above 2^53-1, or a string like '10' coming from unparsed config.","commonSituations":"Reading recallBudget from JSON/YAML config or CLI args without Number() conversion (it arrives as a string); dividing a token budget by a per-item estimate and passing the fractional result directly.","solutions":["Pass an integer >= 0, e.g. recallBudget: 10; zero is valid when you want recall disabled","Coerce and round config-sourced values first: Math.max(0, Math.trunc(Number(raw))) and check Number.isFinite before calling memory()","If computing the budget from a ratio, apply Math.ceil or Math.round to land on an integer"],"exampleFix":"// before (config value came in as string)\nmemory({ namespace: 'notes', ttl: '7d', recallBudget: Number(raw) }); // raw = '10.5'\n\n// after\nmemory({ namespace: 'notes', ttl: '7d', recallBudget: Math.max(0, Math.trunc(Number(raw))) });","handlingStrategy":"validation","validationCode":"function toRecallBudget(raw: unknown): number {\n  const n = typeof raw === 'number' ? raw : Number(raw);\n  if (!Number.isSafeInteger(n) || n < 0) throw new Error(`recallBudget must be a non-negative integer, got ${typeof raw}`);\n  return n;\n}","typeGuard":"function isRecallBudget(value: unknown): value is number { return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0; }","tryCatchPattern":null,"preventionTips":["Coerce numeric config with Number() and Math.trunc at load time","Never pass ratios unrounded into recallBudget","0 is valid — use it deliberately to disable recall"],"tags":["validation","memory","numeric","config"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}