{"record":{"id":"c8ceaf0eed09a3d9","repo":"JuliusBrussee/caveman","slug":"label-key-is-required","errorCode":null,"errorMessage":"${label} key is required","messagePattern":"(.+?) key is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":17394,"sourceCode":"  for (const r of sorted) {\n    const decoded = keys.get(r.signature?.key_id);\n    if (!decoded) return `seq ${r.seq}: no trusted public key for key_id ${String(r.signature?.key_id)}`;\n    const err = verifyReceipt(r, decoded.key, decoded.info.key_id);\n    if (err) return err;\n    if (prev) {\n      if (r.seq !== prev.seq + 1) return `seq ${r.seq}: not strictly after ${prev.seq}`;\n      if (r.prev_receipt_hash !== prev.receipt_hash) return `seq ${r.seq}: prev_receipt_hash does not link to seq ${prev.seq}`;\n      if (r.day <= prev.day) return `seq ${r.seq}: day ${r.day} does not follow ${prev.day}`;\n    }\n    prev = r;\n  }\n  return null;\n}\n\nfunction decodeReceiptKey(info: ReceiptPublicKey, label: string): DecodedReceiptKey {\n  if (!info || typeof info.key_id !== \"string\" || !info.key_id.trim()) throw new Error(`${label} key_id is required`);\n  if (info.alg !== \"Ed25519\") throw new Error(`${label} has unsupported algorithm ${String(info.alg)}`);\n  if (typeof info.key !== \"string\" || !info.key.trim()) throw new Error(`${label} key is required`);\n  const raw = Buffer.from(info.key, \"base64\");\n  if (raw.length !== 32 || raw.toString(\"base64\") !== info.key) throw new Error(`${label} must be a canonical base64 Ed25519 public key`);\n  return { info, raw, key: ed25519PublicKey(raw) };\n}\n\nfunction decodeUniqueKeyring(infos: ReceiptPublicKey[], label: string): Map<string, DecodedReceiptKey> {\n  const keys = new Map<string, DecodedReceiptKey>();\n  for (const [index, info] of infos.entries()) {\n    const decoded = decodeReceiptKey(info, `${label}[${index}]`);\n    if (keys.has(decoded.info.key_id)) throw new Error(`${label} contains duplicate key_id ${decoded.info.key_id}`);\n    keys.set(decoded.info.key_id, decoded);\n  }\n  return keys;\n}\n\nfunction embeddedReceiptKeys(bundle: ReceiptBundle): { current: DecodedReceiptKey; keys: Map<string, DecodedReceiptKey> } {\n  if (bundle.schema !== RECEIPT_BUNDLE_V1 && bundle.schema !== RECEIPT_BUNDLE_V2) throw new Error(`unsupported bundle schema ${String(bundle.schema)}`);\n  if (bundle.verification_coverage !== undefined && bundle.verification_coverage !== INCLUDED_RECEIPTS_ONLY) throw new Error(`unsupported unsigned verification coverage ${String(bundle.verification_coverage)}`);","sourceCodeStart":17376,"sourceCodeEnd":17412,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/5184b3d11ac6a1acb7d44b9bfaa31698157cff97/packages/cli/src/index.ts#L17376-L17412","documentation":"A keyring entry passed the key_id and algorithm checks but its key field is missing, empty, or whitespace-only, so there is no key material to verify receipts with. decodeReceiptKey throws with the entry's label so the offending entry can be located directly.","triggerScenarios":"Keyring entries where the base64 key was dropped during serialization; templates with a key placeholder that was never filled; whitespace-padded keys from copy-paste (the check trims).","commonSituations":"Hand-authored keyring JSON; CI templates interpolating an unset key variable; sanitization steps stripping long opaque strings.","solutions":["Fill in the base64 Ed25519 public key for the entry named in the message","Ensure the variable feeding the template is non-empty before rendering","Re-fetch the published keyring from the issuer"],"exampleFix":"// before\n{ \"key_id\": \"k1\", \"alg\": \"Ed25519\", \"key\": \"\" }\n// after\n{ \"key_id\": \"k1\", \"alg\": \"Ed25519\", \"key\": \"11qYAYKxCrfVS/_TyWQHOg7hcvPapiMlrwIaaPcHURo=\" }","handlingStrategy":"validation","validationCode":"for (const [i, k] of keyring.entries()) {\n  if (typeof k?.key !== 'string' || !k.key.trim()) throw new Error(`keyring[${i}].key is missing`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate keyring completeness before batch verification","Generate keyrings programmatically from issuer artifacts","Fail CI when template placeholders survive into rendered config"],"tags":["crypto","keyring","validation","receipts"],"backgroundTag":"missing-key-value","analyzedSha":"5184b3d11ac6a1acb7d44b9bfaa31698157cff97","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}