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
- Set source to a full relative file path ending in the .capsule filename, e.g. "capsules/kernel-core.capsule"
- Remove trailing slashes, empty values, or `..` components from source
- 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
- Always write source as a relative file path ending in the .capsule filename
- Reject trailing slashes, empty strings, and `..` components when generating sources
- Validate sources with Path::file_name() before shipping the manifest
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
- embedded capsule source does not match package
- embedded distro selects duplicate capsule asset
- must be an absolute path
- bundled executable must have a parent directory
- cannot construct the bundled runtime PATH
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)