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

  1. Set `provenance: "local"` and `consent: "local_only"` on the memory definition (or use the `memory()` builder helper, which validates this).
  2. If you intended a shared memory backend, this runtime does not support one — implement storage in your own tools instead of configuring it here.
  3. 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

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


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)