{"record":{"id":"2e65cff0512f807f","repo":"JuliusBrussee/caveman","slug":"caveman-agent-memory-ttl-must-use-positive-m-h","errorCode":null,"errorMessage":"caveman agent: memory ttl must use positive m, h, or d duration","messagePattern":"caveman agent: memory ttl must use positive m, h, or d duration","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/primitives.ts","lineNumber":277,"sourceCode":"}\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\");\n  }\n  return Object.freeze({\n    kind: \"memory\",\n    namespace: options.namespace,\n    provenance,","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/primitives.ts#L259-L295","documentation":"The memory() primitive validates options.ttl by delegating to memoryTTLMilliseconds(); if that parser throws for any reason, this error is raised instead. Only positive durations expressed with the m (minutes), h (hours), or d (days) units are accepted. Zero, negative, missing-unit, or wrong-unit durations are rejected so records never get a non-positive or ambiguous expiry.","triggerScenarios":"Passing ttl values such as \"0m\", \"-5m\", \"30\", \"30s\", \"30ms\", \"1w\", \"\", or a non-string (number 30, undefined) to memory() — anything memoryTTLMilliseconds() cannot parse into a positive m/h/d duration.","commonSituations":"Copy-pasting a duration like \"500ms\" or \"30s\" from another library's config; forgetting the unit (\"30\"); choosing \"w\" or \"y\" units that this parser does not support; passing a numeric millisecond value where a duration string is expected.","solutions":["Express the ttl with a supported unit and a positive value: \"30m\", \"12h\", or \"7d\"","Convert unsupported units: seconds to minutes (90s -> \"2m\" or \"1m\"), weeks to days (1w -> \"7d\")","Ensure ttl is a string, not a number or undefined — append \"m\" to numeric minute values","Check for zero/negative values, which are rejected even with a valid unit"],"exampleFix":"// before\ndefineMemory({ namespace: \"notes\", ttl: \"30s\" });\ndefineMemory({ namespace: \"notes\", ttl: 86400000 });\n// after\ndefineMemory({ namespace: \"notes\", ttl: \"30m\" });\ndefineMemory({ namespace: \"notes\", ttl: \"24h\" });","handlingStrategy":"validation","validationCode":"const TTL_RE = /^([1-9][0-9]*)(m|h|d)$/;\nfunction isValidTtl(ttl) {\n  return typeof ttl === \"string\" && TTL_RE.test(ttl);\n}\n// before calling: if (!isValidTtl(ttl)) throw new Error(`bad ttl: ${ttl}`);","typeGuard":"function isMemoryTtl(ttl: unknown): ttl is string {\n  return typeof ttl === \"string\" && /^[1-9][0-9]*(m|h|d)$/.test(ttl);\n}","tryCatchPattern":"try {\n  const def = memory({ namespace: \"notes\", ttl });\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"memory ttl must use positive m, h, or d duration\")) {\n    console.error(`Invalid ttl ${JSON.stringify(ttl)}: use e.g. \"30m\", \"12h\", \"7d\"`);\n    return;\n  }\n  throw err;\n}","preventionTips":["Always include a unit suffix (m/h/d) on duration strings","Convert s/ms/week values to the supported units before passing them","Define ttl constants (e.g. const TTL_7D = \"7d\") instead of inline strings","Test the ttl parser with \"0m\", \"-1d\", \"30s\", and \"1w\" to confirm rejection"],"tags":["validation","configuration","duration","memory"],"backgroundTag":"invalid-duration-format","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}