{"record":{"id":"09a6292c89c56539","repo":"JuliusBrussee/caveman","slug":"cave-memory-ttl-invalid","errorCode":"cave_memory_ttl_invalid","errorMessage":"cave_memory_ttl_invalid","messagePattern":"cave_memory_ttl_invalid","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/primitives.ts","lineNumber":251,"sourceCode":"  if (definition.strategy === \"verbatim\") {\n    return definition.recovery === \"exact_ccr\" ? \"exact_ccr\" : \"inline\";\n  }\n  if (definition.strategy === \"json-index\") return \"compress\";\n  return \"page\";\n}\n\nexport interface MemoryDefinition {\n  readonly kind: \"memory\";\n  readonly namespace: string;\n  readonly provenance: \"local\" | \"project\" | \"external\";\n  readonly ttl: string;\n  readonly recallBudget: number;\n  readonly consent: \"local_only\" | \"project_shared\";\n}\n\nexport function memoryTTLMilliseconds(value: string): number {\n  const match = /^([1-9][0-9]*)(m|h|d)$/.exec(value);\n  if (!match) throw new Error(\"cave_memory_ttl_invalid\");\n  const amount = Number(match[1]);\n  const scale = match[2] === \"m\" ? 60_000 : match[2] === \"h\" ? 3_600_000 : 86_400_000;\n  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)}`);","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/primitives.ts#L233-L269","documentation":"memoryTTLMilliseconds parses a human TTL string (e.g. \"30m\", \"12h\", \"7d\") into milliseconds. This first throw fires when the string does not match the required pattern `^[1-9][0-9]*(m|h|d)$` — i.e. it is malformed, has no unit, uses an unsupported unit (s/w/y), starts with 0, is negative, or has separators/whitespace.","triggerScenarios":"Calling `memoryTTLMilliseconds(\"7 days\")`, `memoryTTLMilliseconds(\"30s\")`, `memoryTTLMilliseconds(\"0d\")`, `memoryTTLMilliseconds(\"007h\")`, `memoryTTLMilliseconds(\"12\")` (missing unit), or passing a number/undefined instead of a string.","commonSituations":"Reading TTL from env/config with units like \"s\" or \"w\" that the grammar rejects; localized or spelled-out durations (\"1 day\"); empty string from an unset config value; copy-pasted values with whitespace or trailing punctuation.","solutions":["Format the TTL as an integer (no leading zeros) followed by `m`, `h`, or `d`, e.g. \"30m\", \"24h\", \"14d\".","Convert unsupported units before calling: seconds→minutes (\"90s\"→\"2m\"), weeks→days (\"2w\"→\"14d\").","Validate the string with the same regex client-side before passing it in.","Trim whitespace and strip surrounding quotes from config/env-derived values."],"exampleFix":"// before\nmemoryTTLMilliseconds(process.env.MEM_TTL ?? \"30s\");\n// after\nconst ttl = (process.env.MEM_TTL ?? \"30m\").trim();\nif (!/^[1-9][0-9]*(m|h|d)$/.test(ttl)) throw new Error(\"bad TTL\");\nmemoryTTLMilliseconds(ttl);","handlingStrategy":"validation","validationCode":"const TTL_RE = /^[1-9][0-9]*(m|h|d)$/;\nif (typeof ttl !== \"string\" || !TTL_RE.test(ttl.trim())) {\n  throw new Error(`TTL must match Nm|Nh|Nd, got: ${JSON.stringify(ttl)}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const ms = memoryTTLMilliseconds(ttl);\n} catch (e) {\n  if (e instanceof Error && e.message === \"cave_memory_ttl_invalid\") {\n    console.error(`TTL '${ttl}' is not <integer><m|h|d> (no leading zeros, no s/w units)`);\n  } else throw e;\n}","preventionTips":["Centralize TTL formatting in one helper that emits only `Nm|Nh|Nd` strings.","Restrict config to supported units (m/h/d); convert s/w/y at the config boundary.","Trim and normalize env/config strings before parsing.","Test TTL parsing with boundary values (\"1m\", \"1d\") in CI."],"tags":["ttl","duration","memory","validation"],"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"}