JuliusBrussee/caveman · error · Error

cave_memory_ttl_invalid

Error message

cave_memory_ttl_invalid

What it means

Thrown by memoryTTLMilliseconds() when the TTL string does not match the strict duration grammar ^([1-9][0-9]*)(m|h|d)$ — a positive integer amount with a single unit of m (minutes), h (hours), or d (days). Zero-prefixed, fractional, negative, empty, or suffixed-with-other-unit strings are all rejected before any memory definition is created.

Source

Thrown at packages/agent/src/primitives.ts:251

  if (definition.strategy === "verbatim") {
    return definition.recovery === "exact_ccr" ? "exact_ccr" : "inline";
  }
  if (definition.strategy === "json-index") return "compress";
  return "page";
}

export interface MemoryDefinition {
  readonly kind: "memory";
  readonly namespace: string;
  readonly provenance: "local" | "project" | "external";
  readonly ttl: string;
  readonly recallBudget: number;
  readonly consent: "local_only" | "project_shared";
}

export function memoryTTLMilliseconds(value: string): number {
  const match = /^([1-9][0-9]*)(m|h|d)$/.exec(value);
  if (!match) throw new Error("cave_memory_ttl_invalid");
  const amount = Number(match[1]);
  const scale = match[2] === "m" ? 60_000 : match[2] === "h" ? 3_600_000 : 86_400_000;
  const milliseconds = amount * scale;
  if (!Number.isSafeInteger(amount) || !Number.isSafeInteger(milliseconds) || milliseconds <= 0) {
    throw new Error("cave_memory_ttl_invalid");
  }
  return milliseconds;
}

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)}`);

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Use a plain positive integer followed by exactly one of m, h, or d — e.g. '30m', '12h', '7d'
  2. If the TTL arrives in another unit, convert it first (e.g. Math.ceil(minutes) + 'm') and guard against 0/negative results
  3. Validate config-sourced TTLs with the same regex ^([1-9][0-9]*)(m|h|d)$ at load time so the failure surfaces in config parsing, not agent construction

Example fix

// before
memory({ namespace: 'notes', ttl: '3600s', recallBudget: 10 });

// after
memory({ namespace: 'notes', ttl: '60m', recallBudget: 10 });
Defensive patterns

Strategy: validation

Validate before calling

const TTL_RE = /^([1-9][0-9]*)(m|h|d)$/;
function isTTLString(v: unknown): v is string {
  return typeof v === 'string' && TTL_RE.test(v);
}

Type guard

function isMemoryTTL(value: unknown): value is `${number}${'m'|'h'|'d'}` { return typeof value === 'string' && /^([1-9][0-9]*)(m|h|d)$/.test(value); }

Try / catch

try { memoryTTLMilliseconds(ttl); } catch (e) { if (e instanceof Error && e.message === 'cave_memory_ttl_invalid') throw new ConfigError(`ttl '${mask(ttl)}' must be like '30m', '12h', or '7d'`, { cause: e }); throw e; }

Prevention

When it happens

Trigger: Calling memory({ ttl: ... }) or memoryTTLMilliseconds() directly with values like '0m', '1.5h', '30s', '7days', '-1d', '', ' m', or '04h' — anything with a leading zero, a decimal, a non-m/h/d unit, or extra characters.

Common situations: Configuring a memory namespace from user or YAML/JSON config where TTLs are written in ISO-8601 ('P1D'), seconds ('3600s'), or human prose ('1 day'); copy-pasting a TTL from another library's duration format (Go, .NET, or dayjs).

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/09a6292c89c56539. Report an issue: GitHub.