JuliusBrussee/caveman · error

assembly slot has unknown stability

Error message

assembly slot ${JSON.stringify(slot.id)} has unknown stability ${JSON.stringify(slot.stability)}

What it means

Each assembly slot's `stability` must be one of the declared lifetimes "stable", "session", or "volatile"; it drives how content hashes are tracked across requests in the per-session ledger. assemble() throws this Error when a slot's stability string is not in that set.

Solutions

  1. Use exactly "stable", "session", or "volatile" for each slot's stability.
  2. Map your app's stability vocabulary explicitly (e.g. `immortal -> "stable"`) instead of passing your own strings through.
  3. Trim/lowercase values sourced from config before assigning them.
  4. Type slot literals as AssemblySlot so the stability union is enforced at compile time.

Example fix

// before
slots.push({ id, stability: cfg.ttl === Infinity ? "forever" : "session", content });

// after
const stability = cfg.ttl === Infinity ? "stable" : cfg.ttl === 0 ? "volatile" : "session";
slots.push({ id, stability, content });
Defensive patterns

Strategy: type-guard

Validate before calling

const STABILITIES = new Set(["stable", "session", "volatile"]);
function validateStabilities(slots) {
  for (const s of slots) if (!STABILITIES.has(s.stability)) throw new Error(`slot ${s.id}: bad stability ${s.stability}`);
}

Type guard

function isAssemblyStability(v) {
  return v === "stable" || v === "session" || v === "volatile";
}

Try / catch

try {
  return cave.assemble(opts);
} catch (e) {
  const m = e.message.match(/has unknown stability (".*")/);
  if (m) {
    console.error(`Fix slot stability value ${m[1]} to stable|session|volatile`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing `stability` values like "permanent", "stable " (trailing space), "Stable", or `undefined` — possible when stability is computed from config, or from a JS caller constructing slots without the field.

Common situations: Custom abstractions over the SDK mapping their own stability vocabulary to the SDK's three tiers and missing the mapping; JSON-driven slot definitions with free-form stability strings; typos like "volatile" vs "volitile".

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/e6482d6bfb3c7046. Report an issue: GitHub.

Appendix: source

Thrown at packages/sdk/typescript/src/index.ts:1831

function assemble(cave: Cave, options: AssembleOptions): AssemblyResult {
  const provider = options.provider.trim().toLowerCase();
  const emitCacheHints = options.emitCacheHints ?? "gateway";
  if (!options.model || !options.sessionId) throw new Error("assemble requires model and sessionId");
  if (!["gateway", "self", "none"].includes(emitCacheHints)) {
    throw new Error(`unknown emitCacheHints value ${JSON.stringify(emitCacheHints)}`);
  }

  const ids = new Set<string>();
  let ledger = assemblyHashLedgers.get(cave);
  if (!ledger) {
    ledger = new Map<string, string>();
    assemblyHashLedgers.set(cave, ledger);
  }
  const normalized = options.slots.map((slot) => {
    if (!slot.id || ids.has(slot.id)) throw new Error(`assembly slot id must be non-empty and unique: ${JSON.stringify(slot.id)}`);
    ids.add(slot.id);
    if (!["stable", "session", "volatile"].includes(slot.stability)) {
      throw new Error(`assembly slot ${JSON.stringify(slot.id)} has unknown stability ${JSON.stringify(slot.stability)}`);
    }
    let contentJSON: string;
    try {
      contentJSON = canonicalJSON(slot.content);
    } catch {
      throw new Error(`assembly slot ${JSON.stringify(slot.id)} content is not JSON-serializable`);
    }
    const contentHash = sha256Hex(contentJSON);
    if (slot.stability !== "volatile") {
      const key = `${options.sessionId}\0${slot.id}`;
      const previous = ledger.get(key);
      if (previous !== undefined && previous !== contentHash) throw new AssemblyStabilityError(slot.id);
      ledger.set(key, contentHash);
    }
    return { ...slot, content: JSON.parse(contentJSON) as unknown };
  });

  const ordered = (["stable", "session", "volatile"] as const)

View on GitHub (pinned to 3ee70a1026)