{"record":{"id":"9ab5a938e8141e9d","repo":"astrid-runtime/astrid","slug":"capsule-archive-already-contains-provenance-file","errorCode":null,"errorMessage":"capsule archive already contains {PROVENANCE_FILE}","messagePattern":"capsule archive already contains (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-build/src/artifact.rs","lineNumber":91,"sourceCode":"    #[must_use]\n    pub fn content_digest(&self) -> &str {\n        match self {\n            Self::Unsigned { content_digest } => content_digest,\n            Self::Signed(provenance) => &provenance.content_digest,\n        }\n    }\n}\n\n/// Sign a freshly-created capsule archive with the selected runtime key.\n///\n/// # Errors\n///\n/// Fails when the archive is malformed, already contains a provenance entry,\n/// contains unsafe or duplicate entries, or cannot be replaced atomically.\npub fn sign_archive(archive_path: &Path, keypair: &KeyPair) -> anyhow::Result<VerifiedProvenance> {\n    let (records, envelope) = read_archive(archive_path)?;\n    if envelope.is_some() {\n        bail!(\"capsule archive already contains {PROVENANCE_FILE}\");\n    }\n    let content_digest = digest_records(records)?;\n    let signature = keypair.sign(&signature_message(&content_digest));\n    let provenance = VerifiedProvenance {\n        content_digest: content_digest.clone(),\n        signer: keypair.export_public_key(),\n        signature,\n    };\n    let envelope = ProvenanceEnvelope {\n        schema_version: SCHEMA_VERSION,\n        algorithm: ALGORITHM.to_string(),\n        content_digest,\n        signer: provenance.signer,\n        signature,\n    };\n    rewrite_with_provenance(archive_path, &serde_json::to_vec_pretty(&envelope)?)?;\n    Ok(provenance)\n}","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-build/src/artifact.rs#L73-L109","documentation":"The pointer's release.metadata_blake3 must be exactly 64 lowercase hex characters (a BLAKE3 hex digest). This ensure! fails when the digest field is absent-of-shape: wrong length, uppercase, or non-hex characters, meaning the manifest bytes later could not be integrity-checked against it.","triggerScenarios":"validate_pointer (via parse_channel or enforce_continuity) encounters a ChannelPointer whose release.metadata_blake3 fails is_lower_hex_64 (not 64 chars of [0-9a-f]).","commonSituations":"Uppercase digest pasted from a tool that prints uppercase hex; truncated digest; BLAKE3 vs SHA-256 confusion (64 vs 40/64 hex); placeholder value left in a template.","solutions":["Replace release.metadata_blake3 with the lowercase 64-hex BLAKE3 digest of the release manifest bytes (blake3::hash(bytes).to_hex()).","Recompute the digest from the exact manifest file being published and regenerate the pointer.","Ensure your tooling emits lowercase hex (avoid to_uppercase or base64 mixups)."],"exampleFix":"// before\nmetadata_blake3 = \"ABCDEF...\" // uppercase / wrong length\n// after\nlet digest = blake3::hash(&manifest_bytes).to_hex(); // 64 lowercase hex\nmetadata_blake3 = digest.to_string();","handlingStrategy":"validation","validationCode":"fn valid_blake3_hex(s: &str) -> bool {\n    s.len() == 64 && s.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f'))\n}","typeGuard":"fn is_lower_hex_64(s: &str) -> bool {\n    s.len() == 64 && s.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f'))\n}","tryCatchPattern":"match parse_channel(&bytes, channel, now) {\n    Err(e) if e.to_string().contains(\"metadata BLAKE3 is invalid\") => eprintln!(\"recompute lowercase blake3 hex digest\"),\n    other => other,\n}","preventionTips":["Compute digests with blake3::hash(...).to_hex() into the struct directly","Pre-publish assert is_lower_hex_64 on digest fields","Never paste digests by hand"],"tags":["validation","hash","blake3","signing"],"backgroundTag":"checksum-mismatch","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}