JuliusBrussee/caveman · error
cave_memory_shared_backend_unavailable
cave_memory_shared_backend_unavailable
Error message
cave_memory_shared_backend_unavailable
What it means
`localMemoryTools` throws `cave_memory_shared_backend_unavailable` at tool-setup time when an `AgentDefinition.memory` block does not declare `provenance: "local"` and `consent: "local_only"`. This runtime only implements a local file-backed memory store; any shared/remote memory backend configuration is rejected before the run starts (before any spend), as defense in depth on top of the `memory()` builder check, so hand-built `MemoryDefinition` objects also fail closed.
Solutions
- Set `provenance: "local"` and `consent: "local_only"` on the memory definition (or use the `memory()` builder helper, which validates this).
- If you intended a shared memory backend, this runtime does not support one — implement storage in your own tools instead of configuring it here.
- Fix string casing/typos so both fields match the exact enum values.
Example fix
// before
const def = { memory: { namespace: "notes", provenance: "shared", consent: "local_only" } };
// after
const def = { memory: { namespace: "notes", provenance: "local", consent: "local_only" } }; Defensive patterns
Strategy: validation
Validate before calling
function isLocalMemory(m: { provenance: string; consent: string }): boolean {
return m.provenance === "local" && m.consent === "local_only";
}
if (def.memory && !isLocalMemory(def.memory)) throw new Error("memory must be local/local_only"); Type guard
const isLocalMemoryDef = (m: MemoryDefinition): m is MemoryDefinition & { provenance: "local"; consent: "local_only" } =>
m.provenance === "local" && m.consent === "local_only"; Try / catch
try {
await runAgent(def);
} catch (e) {
if ((e as Error).message === "cave_memory_shared_backend_unavailable") {
console.error("AgentDefinition.memory must be provenance=local, consent=local_only");
process.exitCode = 2;
} else throw e;
} Prevention
- Build memory config with the `memory()` builder instead of hand-writing MemoryDefinition objects.
- Validate agent definitions loaded from JSON/config against the exact enum strings before running.
- Remember this runtime only supports local file-backed memory — never configure shared backends here.
When it happens
Trigger: Passing a hand-built `MemoryDefinition` with `provenance` other than `"local"` or `consent` other than `"local_only"` in an `AgentDefinition`, bypassing the `memory()` builder's own validation.
Common situations: Constructing agent definitions programmatically from JSON/config files that carry shared-backend flags; copying a definition snippet written for a backend-supporting deployment; typos like `provenance: "Local"` or `consent: "localOnly"`.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- MCP changes require the ownership transaction
- : MCP ownership journal failed; native config may already…
- MCP transaction failed and rolled back
- MCP transaction failed and safe recovery was blocked: …
- MCP transaction state kept changing; refusing mutation
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/fa576ac61f82cbde.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/runtime.ts:3116
},
};
}
function validEngineTokenCount(value: unknown): number | undefined {
return Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : undefined;
}
function localMemoryTools(
definition: NonNullable<AgentDefinition["memory"]>,
agentId: string,
config: MemoryStoreConfig | undefined,
): AgentTool<TSchema>[] {
// Defense in depth: memory() already rejects a non-local provenance/consent at
// construction, but a hand-built MemoryDefinition must still fail
// BEFORE the run spends — not inside a tool call — so this throws at tool
// setup, never at execute time.
if (definition.provenance !== "local" || definition.consent !== "local_only") {
throw new Error("cave_memory_shared_backend_unavailable");
}
// Resolved once so a validation failure surfaces at setup, and every entry is
// scoped by (tenant, agentId, namespace): two AgentDefinitions declaring the
// same namespace in one process write to different files and never bleed.
const filePath = memoryFilePath(config, agentId, definition.namespace);
const ttl = memoryTTLMilliseconds(definition.ttl);
const recall: AgentTool<TSchema> = {
name: "cave_memory_search",
label: "cave_memory_search",
description: "Recall relevant local memory from declared namespace. Returns bounded local evidence only.",
parameters: Type.Object({ query: Type.String() }),
executionMode: "parallel",
async execute(_toolCallId, params) {
const query = (params as { query: string }).query;
const now = Date.now();
// Eviction on read: a memory past its declared ttl is neither returned nor
// kept — the durable set is rewritten with only the live entries.
const live = await mutateMemories(filePath, (entries) =>View on GitHub (pinned to 3ee70a1026)