unicity-aos/aos-ce · error · io::Error

embedded distro selects duplicate capsule asset

Error message

embedded distro selects duplicate capsule asset {asset}

What it means

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.

Solutions

  1. Remove the duplicate `[capsule]` entry from the embedded manifest
  2. Or give each duplicate entry a distinct source filename (and matching `{name}.capsule` name field)
  3. Rebuild and re-run install_capsule_fixtures to verify the asset list is unique

Example fix

// before
capsule = [
  { name = "auth", source = "capsules/auth.capsule" },
  { name = "auth", source = "capsules/auth.capsule" }
]
// after
capsule = [
  { name = "auth", source = "capsules/auth.capsule" },
  { name = "auth-legacy", source = "capsules/auth-legacy.capsule" }
]
Defensive patterns

Strategy: validation

Validate before calling

fn has_unique_assets(manifest: &toml::Value) -> bool {
    manifest.get("capsule").and_then(|v| v.as_array())
        .map(|capsules| {
            let mut names: Vec<_> = capsules.iter()
                .filter_map(|c| c.get("source").and_then(|s| s.as_str()))
                .filter_map(|s| std::path::Path::new(s).file_name().map(|f| f.to_os_string()))
                .collect();
            names.sort();
            names.dedup();
            // compare against unfiltered length in real code
            true
        })
        .unwrap_or(false)
}

Try / catch

match capsule_assets_from_manifest() {
    Err(e) if e.to_string().contains("duplicate capsule asset") => {
        eprintln!("Deduplicate the [[capsule]] entries: {e}");
    }
    Err(e) => return Err(e),
    Ok(assets) => assets,
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of unicity-aos/aos-ce@f6f22024fb (2026-09-13). Data as JSON: /api/errors/ebf8555686abd5ac. Report an issue: GitHub.

Appendix: source

Thrown at crates/unicity-aos-bootstrap/src/lib.rs:704

                format!("embedded capsule source is not canonical: {source}"),
            ));
        }
        let asset = relative
            .file_name()
            .expect("validated capsule source has a filename")
            .to_str()
            .ok_or_else(|| {
                io::Error::new(io::ErrorKind::InvalidData, "capsule asset is not UTF-8")
            })?
            .to_owned();
        if asset != format!("{package}.capsule") {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!("embedded capsule source does not match package {package}"),
            ));
        }
        if assets.contains(&asset) {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!("embedded distro selects duplicate capsule asset {asset}"),
            ));
        }
        assets.push(asset);
    }
    Ok(assets)
}

fn validate_capsule_dir(path: &Path, expected: &[String]) -> io::Result<PathBuf> {
    let metadata = fs::symlink_metadata(path).map_err(|error| {
        io::Error::new(
            error.kind(),
            format!(
                "AOS capsule directory is unavailable at {}: {error}",
                path.display()
            ),
        )

View on GitHub (pinned to f6f22024fb)