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

  1. Express the ttl with a supported unit and a positive value: "30m", "12h", or "7d"
  2. Convert unsupported units: seconds to minutes (90s -> "2m" or "1m"), weeks to days (1w -> "7d")
  3. Ensure ttl is a string, not a number or undefined — append "m" to numeric minute values
  4. 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

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


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)