{"record":{"id":"75c30aefd6d4ad56","repo":"JuliusBrussee/caveman","slug":"caveman-agent-invalid-memory-namespace-json-str","errorCode":null,"errorMessage":"caveman agent: invalid memory namespace ${JSON.stringify(options.namespace)}","messagePattern":"caveman agent: invalid memory namespace (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/primitives.ts","lineNumber":269,"sourceCode":"  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)}`);\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  }","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/primitives.ts#L251-L287","documentation":"The `memory()` factory validates that the namespace identifier matches `^[a-z0-9][a-z0-9_-]{0,95}$` — lowercase alphanumerics, hyphens, and underscores, 1–96 chars, starting with a lowercase letter or digit. This error is thrown when the namespace string violates that rule, since namespaces key stored memories and must be a safe, portable identifier.","triggerScenarios":"Calling `memory({ namespace: \"My Project\" })` (uppercase/space), `namespace: \"\"`, `namespace: \"-starts-with-dash\"`, `namespace: \"proj.name\"` (dots not allowed), or a namespace longer than 96 characters.","commonSituations":"Deriving the namespace from a project folder name or git repo name (which often contain uppercase, dots, or spaces); interpolating user IDs with uppercase or special characters; template strings that yield empty namespace when a variable is undefined.","solutions":["Rewrite the namespace to lowercase alphanumerics with `-`/`_` separators, 1–96 chars, e.g. \"my-project\" or \"user_42_notes\".","Normalize programmatically before calling: `namespace.toLowerCase().replace(/[^a-z0-9_-]+/g, \"-\").replace(/^-+/, \"\").slice(0, 96) || \"default\"`.","Don't derive the namespace from raw directory names; use an explicit, sanitized constant per project.","Check length: truncate long slugs to 96 characters."],"exampleFix":"// before\nmemory({ namespace: projectDirName, ... });\n// after\nconst namespace = projectDirName.toLowerCase()\n  .replace(/[^a-z0-9_-]+/g, \"-\").replace(/^-+/, \"\").slice(0, 96) || \"default\";\nmemory({ namespace, ... });","handlingStrategy":"validation","validationCode":"const NS_RE = /^[a-z0-9][a-z0-9_-]{0,95}$/;\nif (!NS_RE.test(namespace)) {\n  throw new Error(`namespace must match [a-z0-9][a-z0-9_-]{0,95}, got: ${namespace}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const mem = memory({ namespace, ttl: \"30d\", recallBudget: 100 });\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith(\"caveman agent: invalid memory namespace\")) {\n    console.error(`namespace '${namespace}' invalid — slugify to lowercase [a-z0-9_-], max 96 chars`);\n  } else throw e;\n}","preventionTips":["Slugify derived names (repo/folder/user) to lowercase kebab-case before use.","Never pass raw directory names or interpolated variables as namespaces without sanitizing.","Enforce a max length of 96 characters and no leading `-`/`_`.","Define namespaces as explicit constants in project config rather than computing them at call sites."],"tags":["memory","namespace","validation","identifier"],"backgroundTag":"invalid-identifier-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"}