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

embedded distro has no capsules

Error message

embedded distro has no capsules

What it means

capsule_assets_from_manifest parses the compile-time-embedded UNICITY_CE_MANIFEST TOML and extracts the `capsule` array. This error is thrown when the embedded manifest either has no top-level `capsule` key at all, or the key exists but is not a TOML array. It signals that the embedded distro manifest is malformed and no capsule assets can be enumerated.

Solutions

  1. Add at least one `[[capsule]]` array-of-tables entry to the embedded manifest (UNICITY_CE_MANIFEST / the distro manifest file feeding it).
  2. Check the key spelling and type: it must be exactly `capsule` and an array of tables (`[[capsule]]`), not `[capsule]` or `capsules`.
  3. If the distro genuinely ships no capsules, treat that as a build-time failure: populate the manifest via the distro build script rather than stubbing it empty.
  4. Regenerate the manifest from the canonical source with the project's build/embed tooling instead of editing it by hand.

Example fix

// before (manifest has no capsule array)
[distro]
version = "1.0"

// after
[distro]
version = "1.0"

[[capsule]]
name = "my-package"
source = "capsules/my-package.capsule"
Defensive patterns

Strategy: validation

Validate before calling

// Rust: validate the embedded manifest shape before calling the API
fn ensure_manifest_has_capsules(manifest_toml: &str) -> Result<(), String> {
    let value: toml::Value =
        toml::from_str(manifest_toml).map_err(|e| format!("manifest TOML invalid: {e}"))?;
    match value.get("capsule").and_then(toml::Value::as_array) {
        Some(arr) if !arr.is_empty() => Ok(()),
        _ => Err("embedded manifest has no `capsule` array-of-tables".into()),
    }
}

Type guard

fn has_capsule_array(manifest: &toml::Value) -> bool {
    manifest.get("capsule").map(toml::Value::is_array).unwrap_or(false)
}

Try / catch

match prepare_unicity_ce_init(...) {
    Err(e) if e.to_string().contains("embedded distro has no capsules") => {
        eprintln!("build bug: embedded CE manifest lacks [[capsule]] entries; regenerate manifest");
    }
    Err(e) => return Err(e),
    Ok(v) => v,
}

Prevention

When it happens

Trigger: Calling capsule_assets_from_manifest (directly or via capsule_dir_with, prepare_unicity_ce_init, install_capsule_fixtures) when UNICITY_CE_MANIFEST lacks a `capsule = [...]` table array — e.g. the manifest only has other sections like [package], or `capsule` is declared as a plain table/string instead of an array of tables.

Common situations: Regenerating or hand-editing the embedded distro manifest and accidentally renaming or dropping the `[[capsule]]` section; a build script emitting a manifest template with no capsule entries; a schema/version change where the key was renamed (e.g. to `capsules`); committing an empty or placeholder manifest fixture.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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

Appendix: source

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

}

const fn runtime_binary_name() -> &'static str {
    RUNTIME_EXECUTABLE_NAMES[0]
}

const fn runtime_daemon_binary_name() -> &'static str {
    RUNTIME_EXECUTABLE_NAMES[1]
}

fn capsule_assets_from_manifest() -> io::Result<Vec<String>> {
    let manifest = UNICITY_CE_MANIFEST
        .parse::<toml::Value>()
        .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
    let capsules = manifest
        .get("capsule")
        .and_then(toml::Value::as_array)
        .ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::InvalidData,
                "embedded distro has no capsules",
            )
        })?;
    let mut assets = Vec::with_capacity(capsules.len());
    for capsule in capsules {
        let package = capsule
            .get("name")
            .and_then(toml::Value::as_str)
            .ok_or_else(|| {
                io::Error::new(io::ErrorKind::InvalidData, "embedded capsule has no name")
            })?;
        let source = capsule
            .get("source")
            .and_then(toml::Value::as_str)
            .ok_or_else(|| {
                io::Error::new(io::ErrorKind::InvalidData, "embedded capsule has no source")
            })?;

View on GitHub (pinned to f6f22024fb)