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

embedded capsule has no name

Error message

embedded capsule has no name

What it means

After locating the `capsule` array, capsule_assets_from_manifest iterates each capsule table and reads its `name` field as a string. This error is thrown when a `[[capsule]]` entry is missing the `name` key or the value is not a string. The name is required because the asset filename must equal `<name>.capsule`.

Solutions

  1. Add a string `name` field to the offending `[[capsule]]` entry matching the capsule's package name.
  2. Ensure `name` is a plain TOML string, not a number or other type (quote it if needed).
  3. Check the entry immediately before/after in the array — the error does not identify which entry failed, so validate each `[[capsule]]` block.
  4. Regenerate the manifest with tooling that guarantees both `name` and `source` are present per entry.

Example fix

// before
[[capsule]]
source = "capsules/foo.capsule"

// after
[[capsule]]
name = "foo"
source = "capsules/foo.capsule"
Defensive patterns

Strategy: validation

Validate before calling

// Rust: check every capsule entry has a string name before calling the API
fn ensure_capsules_named(manifest_toml: &str) -> Result<(), String> {
    let v: toml::Value = toml::from_str(manifest_toml).map_err(|e| e.to_string())?;
    for (i, c) in v.get("capsule").and_then(toml::Value::as_array).unwrap_or(&vec![]).iter().enumerate() {
        if c.get("name").and_then(toml::Value::as_str).map(str::is_empty) != Some(false) {
            return Err(format!("capsule entry #{i} is missing a non-empty string `name`"));
        }
    }
    Ok(())
}

Type guard

fn capsule_name(capsule: &toml::Value) -> Option<&str> {
    capsule.get("name").and_then(toml::Value::as_str).filter(|s| !s.is_empty())
}

Try / catch

match capsule_assets_from_manifest() {
    Err(e) if e.to_string().contains("embedded capsule has no name") => {
        eprintln!("a [[capsule]] entry in the embedded manifest lacks a string `name`; fix the manifest");
    }
    other => other,
}

Prevention

When it happens

Trigger: A `[[capsule]]` entry in UNICITY_CE_MANIFEST omits `name`, or sets `name` to a non-string TOML value (integer, boolean, inline table, array). Raised via capsule_assets_from_manifest when invoked by capsule_dir_with, prepare_unicity_ce_init, or install_capsule_fixtures.

Common situations: Hand-adding a capsule entry and forgetting the name field; a script generating the manifest writing an empty or numeric identifier; a schema migration where `name` was renamed (e.g. `package`); copy-pasting an entry and deleting the name line.

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

Appendix: source

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

    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")
            })?;
        let relative = Path::new(source);
        let mut components = relative.components();
        if components.next() != Some(std::path::Component::Normal(OsStr::new("capsules")))
            || components
                .next()
                .and_then(|component| match component {
                    std::path::Component::Normal(name) => Some(name),
                    _ => None,
                })
                .is_none()
            || components.next().is_some()

View on GitHub (pinned to f6f22024fb)