JuliusBrussee/caveman · error
bundle completeness cannot be attested by unsigned export…
Error message
bundle completeness cannot be attested by unsigned export metadata
What it means
Thrown by embeddedReceiptKeys when an unsigned bundle has completeness_attested set to true. Because the bundle itself is unsigned, nothing cryptographically backs a claim that the export is complete (contains every receipt); the verifier therefore refuses to honor such an attestation, preventing an unverifiable completeness claim from being trusted.
Solutions
- Set completeness_attested to false (or remove it) in the unsigned bundle and re-run verification.
- Use the signed export path if you genuinely need an attested-complete bundle.
- Re-export with the official unsigned export command instead of editing the JSON.
Example fix
// before
{"completeness_attested": true, ...} // unsigned bundle
// after
{"completeness_attested": false, ...} // or use a signed bundle for attestation Defensive patterns
Strategy: validation
Validate before calling
if (!isSignedBundle && bundle.completeness_attested === true) throw new Error('completeness_attested is not allowed on unsigned bundles'); Type guard
function isAttestableBundle(b: { schema: string; completeness_attested?: boolean }): boolean {
return b.schema.includes('signed') || b.completeness_attested !== true;
} Try / catch
try {
verifyBundle(bundle);
} catch (e) {
if ((e as Error).message.includes('completeness')) console.error('unsigned bundles cannot attest completeness; use a signed export');
throw e;
} Prevention
- Never set completeness_attested manually; let the export tooling decide.
- Use the signed export path when a completeness attestation is required.
- Treat unsigned bundles as coverage-limited by design.
When it happens
Trigger: Verifying an unsigned bundle where completeness_attested is true — set by hand-editing the JSON, misusing a signed-export field in an unsigned bundle, or an exporter bug.
Common situations: Copy-pasting fields from a signed bundle template into an unsigned one; toggling the flag hoping to silence a completeness warning; tooling that copies signed export metadata onto unsigned exports.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- ed25519 public key must be 32 bytes, got
- envelope: object kind is required
- envelope: organization scope is required
- contains duplicate key_id
- has unsupported algorithm
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/433b018d6181f6ad.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:17869
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");
return { keys: new Map([[current.info.key_id, pinned]]), trust: "pinned_public_key" };
}View on GitHub (pinned to 3ee70a1026)