JuliusBrussee/caveman · error · Error
caveman agent: memory ttl must use positive m, h, or d durat
Error message
caveman agent: memory ttl must use positive m, h, or d duration
What it means
The memory() builder wraps any cave_memory_ttl_invalid failure from memoryTTLMilliseconds() in this clearer message: the ttl option must be a positive integer duration in m, h, or d units. It fires for both failure modes — wrong format (regex miss) and overflow past safe-integer milliseconds — because the builder intentionally re-raises a user-facing message instead of leaking the lower-level code.
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 27d5a3981a)
Solutions
- Express the TTL as '<positive-int>m|h|d', e.g. '30m', '12h', '7d'
- Convert other units before calling memory(): seconds -> Math.ceil(s/60) + 'm', days -> d + 'd'
- Lint/validate ttl in your config loader against ^([1-9][0-9]*)(m|h|d)$ so mistakes fail at config-parse time
Example fix
// before
memory({ namespace: 'notes', ttl: 'P1D', recallBudget: 10 });
// after
memory({ namespace: 'notes', ttl: '1d', recallBudget: 10 }); Defensive patterns
Strategy: validation
Validate before calling
function toCaveTtl(ms: number): string {
if (!Number.isFinite(ms) || ms <= 0) throw new Error('ttl must be positive milliseconds');
const units: [number, string][] = [[86_400_000, 'd'], [3_600_000, 'h'], [60_000, 'm']];
for (const [scale, suffix] of units) { if (ms % scale === 0) return `${Math.min(ms / scale, 100_000)}${suffix}`; }
return `${Math.ceil(ms / 60_000)}m`;
} Type guard
function isCaveTtl(value: unknown): value is string { return typeof value === 'string' && /^([1-9][0-9]*)(m|h|d)$/.test(value); } Try / catch
try { memory(opts); } catch (e) { if (e instanceof Error && e.message.includes('memory ttl must use positive m, h, or d')) throw new ConfigError(`ttl '${opts.ttl}' invalid; expected e.g. '30m'|'12h'|'7d'`, { cause: e }); throw e; } Prevention
- Centralize TTL construction in one helper
- Forbid seconds/ISO durations in the config schema
- Document the m/h/d grammar next to every ttl field in your config reference
When it happens
Trigger: Calling memory({ ttl }) with '90s', 'P1D', '1w', '0h', '1.5d', '', '3600', or an overflowing value like '999999999999999d'.
Common situations: TTLs authored in ISO-8601 or human form in config ('1 day', 'P1D'); seconds-based durations carried over from cache libraries ('300s'); 'never expires' sentinel overflow values.
Related errors
- cave_memory_ttl_invalid
- caveman agent: invalid memory namespace ${JSON.stringify(opt
- caveman agent: memory recallBudget must be a non-negative in
- cave_budget_denomination_ambiguous
- cave_budget_max_invalid
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/2e65cff0512f807f.
Report an issue: GitHub.