JuliusBrussee/caveman · error

unknown emitCacheHints value

Error message

unknown emitCacheHints value ${JSON.stringify(emitCacheHints)}

What it means

assemble() accepts an optional `emitCacheHints` option that defaults to "gateway" and must be one of "gateway", "self", or "none". Any other value — including typos and wrong-cased strings — is rejected with this Error before any assembly work. It is a strict enum guard on the cache-hint emission policy.

Solutions

  1. Set emitCacheHints to exactly "gateway", "self", or "none" (lowercase).
  2. Normalize the value yourself before calling: `hints.trim().toLowerCase()` if it originates from config or env.
  3. Omit the option entirely to get the "gateway" default.
  4. Type the variable as the exported EmitCacheHints union so invalid values fail at compile time.

Example fix

// before
cave.assemble({ ..., emitCacheHints: process.env.HINTS as string });

// after
const HINTS = ["gateway", "self", "none"] as const;
type Hints = typeof HINTS[number];
const raw = (process.env.HINTS ?? "gateway").toLowerCase() as Hints;
if (!HINTS.includes(raw)) throw new Error(`bad HINTS: ${process.env.HINTS}`);
cave.assemble({ ..., emitCacheHints: raw });
Defensive patterns

Strategy: validation

Validate before calling

const EMIT_CACHE_HINTS = new Set(["gateway", "self", "none"]);
function validHints(v) { return v === undefined || EMIT_CACHE_HINTS.has(v); }

Type guard

function isEmitCacheHints(v) {
  return v === undefined || v === "gateway" || v === "self" || v === "none";
}

Try / catch

try {
  return cave.assemble(opts);
} catch (e) {
  if (e.message.startsWith("unknown emitCacheHints")) {
    opts.emitCacheHints = "gateway"; // safe default
    return cave.assemble(opts);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling assemble() with `emitCacheHints` set to any string not in ["gateway","self","none"] — e.g. "Gateway", "gateway-hints", "cache", or a value read from config/env with unexpected casing or whitespace (note: `emitCacheHints` is used verbatim, only `provider` is lowercased).

Common situations: Mapping an env var like EMIT_CACHE_HINTS=Gateway straight into options; typos like "none" vs "off"; copying an option name from a different SDK version where the enum values differed; JSON config files with inconsistent casing.

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/6a1cb35cf9e9d585. Report an issue: GitHub.

Appendix: source

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

    items: selectedIDs.map((id) => byID.get(id)!),
    tokensUsed,
    tokensBefore,
    tokensSaved,
    deferredCount,
    deferredIds: [...deferredIDs],
    basis: "inferred"
  };
}

const assemblyHashLedgers = new WeakMap<Cave, Map<string, string>>();
const assemblyRequestHeaders = new WeakMap<object, string>();

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 {

View on GitHub (pinned to 3ee70a1026)