JuliusBrussee/caveman · error
caveman agent: memory ttl must use positive m, h, or d…
Error message
caveman agent: memory ttl must use positive m, h, or d duration
What it means
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.
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
Example fix
// before
defineMemory({ namespace: "notes", ttl: "30s" });
defineMemory({ namespace: "notes", ttl: 86400000 });
// after
defineMemory({ namespace: "notes", ttl: "30m" });
defineMemory({ namespace: "notes", ttl: "24h" }); Defensive patterns
Strategy: validation
Validate before calling
const TTL_RE = /^([1-9][0-9]*)(m|h|d)$/;
function isValidTtl(ttl) {
return typeof ttl === "string" && TTL_RE.test(ttl);
}
// before calling: if (!isValidTtl(ttl)) throw new Error(`bad ttl: ${ttl}`); Type guard
function isMemoryTtl(ttl: unknown): ttl is string {
return typeof ttl === "string" && /^[1-9][0-9]*(m|h|d)$/.test(ttl);
} Try / catch
try {
const def = memory({ namespace: "notes", ttl });
} catch (err) {
if (err instanceof Error && err.message.includes("memory ttl must use positive m, h, or d duration")) {
console.error(`Invalid ttl ${JSON.stringify(ttl)}: use e.g. "30m", "12h", "7d"`);
return;
}
throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "invalid duration" / "failed to parse duration": why your timeout, interval, or TTL string is rejected and which formats each library accepts — this error's family across 32 libraries.
Related errors
- cave_memory_ttl_invalid
- caveman agent: memory recallBudget must be a non-negative…
- apiKey, baseURL, and agent are required
- ASGI context resolver or request bounds are invalid
- bedrock endpoint must be runtime or mantle
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/2e65cff0512f807f.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/primitives.ts:277
}
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");
}
return Object.freeze({
kind: "memory",
namespace: options.namespace,
provenance,View on GitHub (pinned to 3ee70a1026)