{"record":{"id":"880a31975b0f6771","repo":"astrid-runtime/astrid","slug":"capsule-content-tree-contains-duplicate-paths","errorCode":null,"errorMessage":"capsule content tree contains duplicate paths","messagePattern":"capsule content tree contains duplicate paths","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-build/src/artifact.rs","lineNumber":332,"sourceCode":"            break;\n        }\n        hasher.update(&buffer[..count]);\n        read = read.saturating_add(count as u64);\n    }\n    if read != size {\n        bail!(\"capsule entry '{path}' size changed while hashing\");\n    }\n    Ok(ContentRecord {\n        path,\n        size,\n        hash: *hasher.finalize().as_bytes(),\n    })\n}\n\nfn digest_records(mut records: Vec<ContentRecord>) -> anyhow::Result<String> {\n    records.sort_unstable_by(|left, right| left.path.cmp(&right.path));\n    if records.windows(2).any(|pair| pair[0].path == pair[1].path) {\n        bail!(\"capsule content tree contains duplicate paths\");\n    }\n    let mut hasher = blake3::Hasher::new();\n    hasher.update(CONTENT_DOMAIN);\n    for record in records {\n        let path = record.path.as_bytes();\n        hasher.update(&(path.len() as u64).to_le_bytes());\n        hasher.update(path);\n        hasher.update(&record.size.to_le_bytes());\n        hasher.update(&record.hash);\n    }\n    Ok(hasher.finalize().to_hex().to_string())\n}\n\nfn signature_message(content_digest: &str) -> Vec<u8> {\n    let mut message =\n        Vec::with_capacity(SIGNATURE_DOMAIN.len().saturating_add(content_digest.len()));\n    message.extend_from_slice(SIGNATURE_DOMAIN);\n    message.extend_from_slice(content_digest.as_bytes());","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-build/src/artifact.rs#L314-L350","documentation":"Fails the capsule digest computation in digest_records after sorting ContentRecords by path and detecting two adjacent records with the same path. It fires when the content collection stage produced two entries for one file — typically because overlapping include rules or a symlink resolution mapped two inputs to the same normalized path. Callers sign_archive and verify_records abort rather than emit a hash over an ambiguous manifest.","triggerScenarios":"Thrown at crates/astrid-build/src/artifact.rs:332 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Fix Capsule.toml / asset layout so every content file has a unique path","Rebuild the capsule; duplicate paths indicate a packaging bug","Report the duplicate path to the capsule author"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}