JuliusBrussee/caveman · error

cave_cache_volatile_stable_slot

cave_cache_volatile_stable_slot

Error message

cave_cache_volatile_stable_slot

What it means

Cache-safety guard for plans with segment routing: thrown when an efficiency plan has non-empty segment_routes but the stable prefix slot of the run — instructions and tool definitions — is volatile (volatileStablePrefix(instructions, definition.tools) is true). Prompt-cache stability depends on the leading segments being byte-stable across calls; if dynamic content (timestamps, per-request data) occupies the stable slot, cache digests and any warm-prefix accounting would be lies, so the run refuses.

Source

Thrown at packages/agent/src/runtime.ts:1405

      provider: routedModel.provider,
      model: routedModel.id,
      api: routedModel.api,
      prefixDigest,
      buildIdentity: buildIdentity ?? null,
      lockedPlanSHA256: buildIdentity?.planSha256 ?? null,
      executionPlanSHA256: efficiencyPlan === undefined
        ? null
        : sha256(stableStringify(efficiencyPlan)),
    }));
    if (conversation?.fingerprint !== undefined &&
        conversation.fingerprint !== conversationFingerprint) {
      conversation.cachePrefixDigest = undefined;
      conversation.providerFrozen = undefined;
      conversation.originalFrozen = undefined;
    }
    if (conversation) conversation.fingerprint = conversationFingerprint;
    if (efficiencyPlan?.segment_routes.length && volatileStablePrefix(instructions, definition.tools)) {
      throw new Error("cave_cache_volatile_stable_slot");
    }
    // Every x-cave-* header exists for the Caveman gateway: x-cave-api-key is
    // an account credential, and agent/workflow/session/cache-epoch/prefix
    // digest/context bill/build+plan digests are account-linked identifiers and
    // internal telemetry. A request that does not go through the gateway goes to
    // a third party, so it carries none of them.
    const headers = gatewayActive
      ? runtimeHeaders(
        definition.id,
        options.workflow ?? definition.id,
        sessionId,
        buildIdentity,
        bill,
        appliedPlan.appliedTransformIDs,
        prefixDigest,
        conversationFingerprint,
        executionContext.invocationState.batch?.apiKey,
        executionContext.invocationState.batch === undefined ? undefined : executionContext.invocationTrace,

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Move dynamic content out of the stable prefix: put it in the run input (a later, routable segment) instead of instructions
  2. Make tool definitions static: move per-run parameters out of tool names/descriptions and into tool call parameters
  3. If the prompt must change per run, place the volatile part after the stable segments so segment routing can isolate it
  4. Re-lock the build after prompt changes so plan segments match the new prompt shape

Example fix

// before
const agent = defineAgent({
  instructions: `You are helper. Today is ${new Date().toISOString()}.`,
  tools: [searchTool],
});
await agent.run(userInput, { candidatePlan });

// after
const agent = defineAgent({
  instructions: "You are helper.",
  tools: [searchTool],
});
await agent.run(`Today is ${new Date().toISOString()}\n\n${userInput}`, {
  candidatePlan,
});
Defensive patterns

Strategy: validation

Validate before calling

// Reject obviously volatile content in the stable prefix before running with a plan.
const VOLATILE = /\$\{?(Date\.now|new Date|Math\.random|Date\.)|\d{4}-\d{2}-\d{2}T/;
function assertStablePrefix(instructions: string, tools: readonly ToolDefinition[]) {
  if (VOLATILE.test(instructions) || tools.some(t => VOLATILE.test(t.description ?? ""))) {
    throw new Error("volatile content in stable prompt prefix; move it into run input");
  }
}

Type guard

const hasVolatilePrefix = (instructions: string, tools: readonly ToolDefinition[]): boolean =>
  VOLATILE.test(instructions) || tools.some(t => VOLATILE.test(t.description ?? ""));

Prevention

When it happens

Trigger: Running with a locked build / candidate plan whose segment_routes route later segments, while the agent's instructions string or a tool description embeds per-run dynamic content (Date.now(), request ids, user text) in the leading stable position.

Common situations: Templating a timestamp or the current user's name directly into the system prompt; putting volatile values into the first tool's description; reusing a plan locked against a different (stable) prompt shape after the prompt was made dynamic.

Related errors


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