JuliusBrussee/caveman · error
unsupported bundle schema ${String(bundle.schema)}
Error message
unsupported bundle schema ${String(bundle.schema)} What it means
embeddedReceiptKeys accepts only bundles whose schema is "caveman.receipt-bundle.v1" or "caveman.receipt-bundle.v2". Any other value (null, a typo, or a newer version) is rejected before key decoding, because the verifier has no parsing rules for unknown schema versions.
Source
Thrown at packages/cli/src/index.ts:17411
if (info.alg !== "Ed25519") throw new Error(`${label} has unsupported algorithm ${String(info.alg)}`);
if (typeof info.key !== "string" || !info.key.trim()) throw new Error(`${label} key is required`);
const raw = Buffer.from(info.key, "base64");
if (raw.length !== 32 || raw.toString("base64") !== info.key) throw new Error(`${label} must be a canonical base64 Ed25519 public key`);
return { info, raw, key: ed25519PublicKey(raw) };
}
function decodeUniqueKeyring(infos: ReceiptPublicKey[], label: string): Map<string, DecodedReceiptKey> {
const keys = new Map<string, DecodedReceiptKey>();
for (const [index, info] of infos.entries()) {
const decoded = decodeReceiptKey(info, `${label}[${index}]`);
if (keys.has(decoded.info.key_id)) throw new Error(`${label} contains duplicate key_id ${decoded.info.key_id}`);
keys.set(decoded.info.key_id, decoded);
}
return keys;
}
function embeddedReceiptKeys(bundle: ReceiptBundle): { current: DecodedReceiptKey; keys: Map<string, DecodedReceiptKey> } {
if (bundle.schema !== RECEIPT_BUNDLE_V1 && bundle.schema !== RECEIPT_BUNDLE_V2) throw new Error(`unsupported bundle schema ${String(bundle.schema)}`);
if (bundle.verification_coverage !== undefined && bundle.verification_coverage !== INCLUDED_RECEIPTS_ONLY) throw new Error(`unsupported unsigned verification coverage ${String(bundle.verification_coverage)}`);
if (bundle.completeness_attested === true) throw new Error("bundle completeness cannot be attested by unsigned export metadata");
const current = decodeReceiptKey(bundle.public_key, "public_key");
if (bundle.public_keys !== undefined && !Array.isArray(bundle.public_keys)) throw new Error("public_keys must be an array");
if (bundle.schema === RECEIPT_BUNDLE_V2 && (!Array.isArray(bundle.public_keys) || bundle.public_keys.length === 0)) throw new Error("v2 bundle requires public_keys");
const keys = decodeUniqueKeyring(bundle.public_keys ?? [], "public_keys");
const currentInRing = keys.get(current.info.key_id);
if (currentInRing && !currentInRing.raw.equals(current.raw)) throw new Error(`public_key conflicts with public_keys entry ${current.info.key_id}`);
if (bundle.schema === RECEIPT_BUNDLE_V2 && !currentInRing) throw new Error("v2 public_keys must include public_key");
if (!currentInRing) keys.set(current.info.key_id, current);
return { current, keys };
}
async function pinnedReceiptKeys(file: string, current: DecodedReceiptKey): Promise<{ keys: Map<string, DecodedReceiptKey>; trust: string }> {
const source = (await readFile(file, "utf8")).trim();
if (!source.startsWith("{")) {
const pinned = decodeReceiptKey({ ...current.info, key: source }, "--pubkey");
if (!pinned.raw.equals(current.raw)) throw new Error("bundle public key does not match the published --pubkey");View on GitHub (pinned to 5184b3d11a)
Solutions
- Upgrade the verifying CLI to a release that knows the bundle's schema version
- Regenerate the bundle with schema "caveman.receipt-bundle.v1" or "caveman.receipt-bundle.v2"
- Inspect the field first: jq .schema bundle.json and compare against the supported literals
Example fix
// before
{ "schema": "receipts-v2", ... }
// after
{ "schema": "caveman.receipt-bundle.v2", ... } Defensive patterns
Strategy: type-guard
Validate before calling
const SUPPORTED = new Set(["caveman.receipt-bundle.v1", "caveman.receipt-bundle.v2"]);
if (!SUPPORTED.has(String(bundle.schema))) {
throw new Error(`bundle schema ${String(bundle.schema)} not supported by this CLI version`);
} Type guard
function isSupportedBundleSchema(v: unknown): v is "caveman.receipt-bundle.v1" | "caveman.receipt-bundle.v2" {
return v === "caveman.receipt-bundle.v1" || v === "caveman.receipt-bundle.v2";
} Try / catch
try { await verifyBundle(bundle); }
catch (e) {
if (/unsupported bundle schema/.test(String((e as Error).message))) {
log(`bundle schema too new/unknown — upgrade the verifying CLI`);
return;
}
throw e;
} Prevention
- Pin producer and verifier CLI versions together in CI so schemas stay compatible
- Check bundle.schema with jq before passing bundles to the verifier
- Surface the CLI's supported schema versions in your pipeline's preflight
When it happens
Trigger: Running `caveman receipts verify` on a bundle whose schema field is anything other than the two supported literals, e.g. a future "caveman.receipt-bundle.v3" produced by a newer release, or a hand-built bundle with a misspelled schema.
Common situations: Version skew: bundle produced by a newer caveman CLI than the one verifying; hand-authored bundles; CI pinned to an old CLI version while producers upgraded.
Related errors
- ${label} key_id is required
- ${label} key is required
- ${label} contains duplicate key_id ${decoded.info.key_id}
- unsupported unsigned verification coverage ${String(bundle.v
- bundle completeness cannot be attested by unsigned export me
AI-assisted analysis of JuliusBrussee/caveman@5184b3d11a (2026-08-18).
Data as JSON: /api/errors/134b09af7428f7c0.
Report an issue: GitHub.