{"record":{"id":"ebf8555686abd5ac","repo":"unicity-aos/aos-ce","slug":"embedded-distro-selects-duplicate-capsule-asset-asset","errorCode":null,"errorMessage":"embedded distro selects duplicate capsule asset {asset}","messagePattern":"embedded distro selects duplicate capsule asset (.+?)","errorType":"validation","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/unicity-aos-bootstrap/src/lib.rs","lineNumber":704,"sourceCode":"                format!(\"embedded capsule source is not canonical: {source}\"),\n            ));\n        }\n        let asset = relative\n            .file_name()\n            .expect(\"validated capsule source has a filename\")\n            .to_str()\n            .ok_or_else(|| {\n                io::Error::new(io::ErrorKind::InvalidData, \"capsule asset is not UTF-8\")\n            })?\n            .to_owned();\n        if asset != format!(\"{package}.capsule\") {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\"embedded capsule source does not match package {package}\"),\n            ));\n        }\n        if assets.contains(&asset) {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\"embedded distro selects duplicate capsule asset {asset}\"),\n            ));\n        }\n        assets.push(asset);\n    }\n    Ok(assets)\n}\n\nfn validate_capsule_dir(path: &Path, expected: &[String]) -> io::Result<PathBuf> {\n    let metadata = fs::symlink_metadata(path).map_err(|error| {\n        io::Error::new(\n            error.kind(),\n            format!(\n                \"AOS capsule directory is unavailable at {}: {error}\",\n                path.display()\n            ),\n        )","sourceCodeStart":686,"sourceCodeEnd":722,"githubUrl":"https://github.com/unicity-aos/aos-ce/blob/f6f22024fb1e8d122f28a1b4a9f75aee448ae839/crates/unicity-aos-bootstrap/src/lib.rs#L686-L722","documentation":"Thrown by capsule_assets_from_manifest when two or more `[capsule]` entries in the embedded manifest resolve to the same asset filename. The library requires each selected capsule asset to be unique so the capsule set is unambiguous; duplicates would silently overwrite or double-install the same file.","triggerScenarios":"Adding a second `[capsule]` entry whose `source` filename duplicates an earlier entry (e.g. two packages both pointing at capsules/foo.capsule, or the same entry duplicated). Fails on any capsule_dir_with / prepare_unicity_ce_init / install_capsule_fixtures call.","commonSituations":"Copy-pasting an existing capsule entry in the embedded manifest to add a variant but forgetting to change the filename; two package entries accidentally sharing one asset; merge conflicts reintroducing a duplicate entry.","solutions":["Remove the duplicate `[capsule]` entry from the embedded manifest","Or give each duplicate entry a distinct source filename (and matching `{name}.capsule` name field)","Rebuild and re-run install_capsule_fixtures to verify the asset list is unique"],"exampleFix":"// before\ncapsule = [\n  { name = \"auth\", source = \"capsules/auth.capsule\" },\n  { name = \"auth\", source = \"capsules/auth.capsule\" }\n]\n// after\ncapsule = [\n  { name = \"auth\", source = \"capsules/auth.capsule\" },\n  { name = \"auth-legacy\", source = \"capsules/auth-legacy.capsule\" }\n]","handlingStrategy":"validation","validationCode":"fn has_unique_assets(manifest: &toml::Value) -> bool {\n    manifest.get(\"capsule\").and_then(|v| v.as_array())\n        .map(|capsules| {\n            let mut names: Vec<_> = capsules.iter()\n                .filter_map(|c| c.get(\"source\").and_then(|s| s.as_str()))\n                .filter_map(|s| std::path::Path::new(s).file_name().map(|f| f.to_os_string()))\n                .collect();\n            names.sort();\n            names.dedup();\n            // compare against unfiltered length in real code\n            true\n        })\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match capsule_assets_from_manifest() {\n    Err(e) if e.to_string().contains(\"duplicate capsule asset\") => {\n        eprintln!(\"Deduplicate the [[capsule]] entries: {e}\");\n    }\n    Err(e) => return Err(e),\n    Ok(assets) => assets,\n}","preventionTips":["Keep the [[capsule]] array deduplicated; never copy-paste entries without changing name and source","Add a unit test asserting capsule_assets_from_manifest returns unique assets","Review merge conflicts on the manifest for reintroduced entries"],"tags":["rust","manifest","duplicate","capsules"],"backgroundTag":"schema-validation-failed","analyzedSha":"f6f22024fb1e8d122f28a1b4a9f75aee448ae839","analyzedAt":"2026-09-13T03:04:44.565Z","contentChangedAt":"2026-09-13T03:04:44.565Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}