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

embedded distro has no capsules

Error message

embedded distro has no capsules

What it means

unicity_ce_capsule_names parses the embedded distribution manifest as TOML and requires a top-level `capsule` array. If the manifest is present but has no `capsule` key (or it is not an array), the function maps that to an InvalidData io::Error with this message. It signals that the bundled distro manifest is structurally invalid for migration.

Solutions

  1. Rebuild the bootstrap distro so its embedded manifest includes a `capsule` array of tables
  2. Inspect the embedded manifest with `toml` tooling to confirm the `capsule` key exists and is an array
  3. Verify the packaging pipeline is embedding the correct manifest file and not a stub/older version

Example fix

# before (manifest.toml embedded in distro)
[meta]
version = 1

# after
[meta]
version = 1

[[capsule]]
name = "core"
Defensive patterns

Strategy: validation

Validate before calling

fn manifest_has_capsules(manifest: &str) -> bool {
    toml::from_str::<toml::Value>(manifest)
        .ok()
        .and_then(|v| v.get("capsule").and_then(|c| c.as_array()).map(|a| !a.is_empty()))
        .unwrap_or(false)
}

Type guard

fn has_capsule_array(manifest: &toml::Value) -> Option<&Vec<toml::Value>> {
    manifest.get("capsule").and_then(|c| c.as_array())
}

Try / catch

match unicity_ce_capsule_names() {
    Ok(names) => proceed(names),
    Err(e) if e.to_string().contains("embedded distro has no capsules") =>
        eprintln!("distro manifest missing capsule section; rebuild distro"),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling archive_inactive_activation_state (which calls unicity_ce_capsule_names) against a bootstrap distro whose embedded manifest TOML lacks a `[capsule]` array, or defines `capsule` as a table/string instead of an array of tables.

Common situations: A distro image built from an older manifest schema before the `capsule` section was introduced; a hand-edited or truncated embedded manifest; packaging tooling stripping the section during build.

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/bfac5155b1526c6d. Report an issue: GitHub.

Appendix: source

Thrown at crates/unicity-aos-bootstrap/src/migration.rs:462

        if name != OsStr::new("default.toml") {
            inactive_profiles.push(PathBuf::from("etc/profiles").join(name));
        }
    }
    for relative in inactive_profiles {
        archive_path(staging, &relative, entries)?;
    }
    Ok(())
}

fn unicity_ce_capsule_names() -> io::Result<HashSet<String>> {
    let manifest = crate::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",
            )
        })?;
    capsules
        .iter()
        .map(|capsule| {
            capsule
                .get("name")
                .and_then(toml::Value::as_str)
                .map(str::to_owned)
                .ok_or_else(|| {
                    io::Error::new(io::ErrorKind::InvalidData, "embedded capsule has no name")
                })
        })
        .collect()
}

View on GitHub (pinned to f6f22024fb)