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
- Use exactly "stable", "session", or "volatile" for each slot's stability.
- Map your app's stability vocabulary explicitly (e.g. `immortal -> "stable"`) instead of passing your own strings through.
- Trim/lowercase values sourced from config before assigning them.
- 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
- Annotate slot literals as AssemblySlot[] so the stability union is checked at compile time.
- Map your app's TTL vocabulary to the three SDK tiers in one place.
- Never pass raw config strings as stability without normalization.
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
- assembly slot id must be non-empty and unique
- assembly slot has unknown stability
- unknown emitCacheHints value
- assemble requires model and session_id
- assemble requires model and sessionId
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)