{"record":{"id":"6cb7f72ae28dc39a","repo":"astrid-runtime/astrid","slug":"capsule-archive-contains-duplicate-entry-path","errorCode":null,"errorMessage":"capsule archive contains duplicate entry '{path}'","messagePattern":"capsule archive contains duplicate entry '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-build/src/artifact.rs","lineNumber":260,"sourceCode":"    read_archive_reader(GzDecoder::new(file))\n}\n\nfn read_archive_reader<R: Read>(\n    reader: R,\n) -> anyhow::Result<(Vec<ContentRecord>, Option<Vec<u8>>)> {\n    let mut archive = tar::Archive::new(reader);\n    let mut records = Vec::new();\n    let mut envelope = None;\n    let mut seen = HashSet::new();\n\n    for entry in archive\n        .entries()\n        .context(\"failed to read capsule archive\")?\n    {\n        let mut entry = entry.context(\"failed to read capsule archive entry\")?;\n        let path = normalized_entry_path(&entry)?;\n        if !seen.insert(path.clone()) {\n            bail!(\"capsule archive contains duplicate entry '{path}'\");\n        }\n        let kind = entry.header().entry_type();\n        if kind.is_dir() {\n            continue;\n        }\n        if !kind.is_file() {\n            bail!(\"capsule archive contains unsupported entry '{path}'\");\n        }\n        if path == PROVENANCE_FILE {\n            if entry.size() > 64 * 1024 {\n                bail!(\"capsule provenance envelope exceeds 64 KiB\");\n            }\n            let mut bytes = Vec::new();\n            entry\n                .read_to_end(&mut bytes)\n                .context(\"failed to read capsule provenance\")?;\n            envelope = Some(bytes);\n            continue;","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-build/src/artifact.rs#L242-L278","documentation":"After identity checks, verify_release_manifest requires the manifest's version, tag, source_commit and release_workflow_identity to equal the corresponding fields of the signed channel pointer. This ensures the manifest actually describes the exact release the signed pointer attests to. A mismatch means the manifest and pointer are from different releases or one of them was tampered with or stale.","triggerScenarios":"verify_release_manifest is called with a parsed ReleaseManifest where any of manifest.version, manifest.tag, manifest.source_commit, or manifest.release_workflow_identity differs from pointer.release's matching field.","commonSituations":"Cache/CDN serving a manifest for version N-1 while the pointer is for N; manifest regenerated (new commit) without re-issuing the signed pointer; a pointer replayed after a re-release of the same version; partial rollback where pointer and manifest updated at different times.","solutions":["Re-fetch both the channel pointer and the manifest from the authoritative source and retry (rules out stale caches).","If the release was rebuilt, regenerate both the manifest and the signed pointer from the same build so version/tag/source_commit/workflow_identity agree.","Fix the publish pipeline to update the pointer atomically with the manifest."],"exampleFix":"# before: pointer for 1.2.3, manifest says\nversion = \"1.2.2\"\n# after\nversion = \"1.2.3\"\ntag = \"v1.2.3\"\nsource_commit = \"<same as pointer.release.source_commit>\"","handlingStrategy":"retry","validationCode":"fn manifest_matches_pointer(m: &ReleaseManifest, p: &ChannelPointer) -> bool {\n    m.version == p.release.version && m.tag == p.release.tag\n        && m.source_commit == p.release.source_commit\n        && m.release_workflow_identity == p.release.release_workflow_identity\n}","typeGuard":null,"tryCatchPattern":"match verify_release_manifest(&bytes, &pointer) {\n    Err(e) if e.to_string().contains(\"does not match the signed channel pointer\") => {\n        // refresh both artifacts from origin once, then retry\n    },\n    other => other,\n}","preventionTips":["Publish pointer and manifest atomically","Make per-version manifests immutable","Use short CDN TTLs or content-addressed URLs"],"tags":["manifest","validation","integrity"],"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"}