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

embedded capsule source has no asset

Error message

embedded capsule source has no asset

What it means

Thrown by materialize_manifest when a capsule entry's `source` string has no final path component — i.e. Path::file_name() returns None. This happens for sources ending in `..`, `/`, or empty/root-like paths, so the library cannot derive the asset filename to join with the capsule directory.

Solutions

  1. Set source to a full relative file path ending in the .capsule filename, e.g. "capsules/kernel-core.capsule"
  2. Remove trailing slashes, empty values, or `..` components from source
  3. Regenerate the manifest programmatically to guarantee each source ends with a concrete filename

Example fix

// before
source = "capsules/"
// after
source = "capsules/kernel-core.capsule"
Defensive patterns

Strategy: validation

Validate before calling

fn source_has_asset(source: &str) -> bool {
    std::path::Path::new(source).file_name().is_some()
}

Type guard

fn has_file_name(source: &str) -> bool {
    !source.is_empty()
        && std::path::Path::new(source).file_name()
            .map(|f| f != ".." && f != ".")
            .unwrap_or(false)
}

Try / catch

match bootstrap::ensure_unicity_ce_manifest(&home) {
    Err(e) if e.to_string().contains("has no asset") => {
        eprintln!("A capsule `source` must be a file path with a filename: {e}");
    }
    Err(e) => return Err(e),
    Ok(manifest) => manifest,
}

Prevention

When it happens

Trigger: An embedded manifest entry with source="capsules/", source="", source="..", or a path terminating in a parent/curdir component; materialize_manifest cannot extract an asset name and fails during ensure_unicity_ce_manifest.

Common situations: Template/variable substitution in the manifest producing a directory path or empty string; hand-edited source values with a trailing slash; scripts generating sources programmatically and omitting the filename.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

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

        .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
    let capsules = manifest
        .get_mut("capsule")
        .and_then(toml::Value::as_array_mut)
        .ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::InvalidData,
                "embedded distro has no capsules",
            )
        })?;
    for capsule in capsules {
        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 asset = Path::new(source).file_name().ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::InvalidData,
                "embedded capsule source has no asset",
            )
        })?;
        let absolute = capsule_dir.join(asset);
        let absolute = absolute.to_str().ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::InvalidInput,
                "AOS capsule directory must be valid UTF-8 for the TOML manifest",
            )
        })?;
        capsule["source"] = toml::Value::String(absolute.to_owned());
    }
    toml::to_string_pretty(&manifest)
        .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))
}

#[cfg(test)]

View on GitHub (pinned to f6f22024fb)