unicity-aos/aos-ce · error · io::Error
capsule asset is not UTF-8
Error message
capsule asset is not UTF-8
What it means
After path validation, the library converts the capsule asset's filename to a Rust str to use as an embedded asset key. This error is thrown when that filename is not valid UTF-8 (Rust OsStr/Path are not guaranteed UTF-8). Practically it means the capsule file on disk has a non-UTF-8 name, which cannot be used as an embedded asset identifier.
Solutions
- Rename the capsule file so its name is valid UTF-8, ideally plain ASCII matching `<package>.capsule`.
- Regenerate the capsule file with build tooling that uses ASCII identifiers for package names.
- Check that the manifest's source string itself is clean UTF-8 TOML (a valid TOML string already is; the corruption is typically on the filesystem side or from escaped bytes).
- Audit manifest generation scripts for byte-level filename handling (e.g. shelling out with raw locale-dependent output).
Example fix
// before (filename with non-UTF-8 bytes) $ mv capsules/foo.capsule $'capsules/fo\xffo.capsule' // after $ mv capsules/foo.capsule capsules/foo.capsule # plain ASCII, matches package name "foo"
Defensive patterns
Strategy: validation
Validate before calling
// Rust: verify asset filenames are valid UTF-8 before invoking the API
fn ensure_utf8_asset_names(manifest_toml: &str) -> Result<(), String> {
let v: toml::Value = toml::from_str(manifest_toml).map_err(|e| e.to_string())?;
for c in v.get("capsule").and_then(toml::Value::as_array).unwrap_or(&vec![]) {
if let Some(src) = c.get("source").and_then(toml::Value::as_str) {
let name = std::path::Path::new(src).file_name().unwrap_or_default();
if name.to_str().is_none() {
return Err(format!("asset filename for {src} is not UTF-8"));
}
}
}
Ok(())
} Type guard
fn is_utf8_file_name(src: &str) -> bool {
std::path::Path::new(src).file_name().map(|n| n.to_str().is_some()).unwrap_or(false)
} Try / catch
match capsule_assets_from_manifest() {
Err(e) if e.to_string().contains("capsule asset is not UTF-8") => {
eprintln!("{e}; rename the capsule file to a UTF-8 (ASCII) name matching <package>.capsule");
}
other => other,
} Prevention
- Build capsule files with ASCII-only package identifiers so filenames are always valid UTF-8.
- Avoid renaming capsule files with shell scripts under non-UTF-8 locales; set LC_ALL=C.UTF-8 in build environments.
- Validate filenames (e.g. reject non-ASCII bytes) when ingesting capsule artifacts from external systems.
When it happens
Trigger: A `[[capsule]]` source resolving to a filename containing raw non-UTF-8 bytes (e.g. Latin-1 encoded characters or invalid byte sequences created on some filesystems), passed through capsule_assets_from_manifest via capsule_dir_with, prepare_unicity_ce_init, or install_capsule_fixtures.
Common situations: A capsule file renamed on a filesystem with a non-UTF-8 locale/encoding; a script writing filenames from unvalidated bytes; files transferred from systems using legacy codepages; accidental binary corruption of a 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
- AOS capsule directory contains a non-UTF-8 entry
- AOS capsule directory must be valid UTF-8 for the TOML…
- imported principal home contains a non-UTF-8 name
- imported capsule installation contains a non-UTF-8 name
- product release runtime bin contains a non-UTF-8 entry
AI-assisted analysis of unicity-aos/aos-ce@f6f22024fb (2026-09-13).
Data as JSON: /api/errors/ea2d4c9e7f016e80.
Report an issue: GitHub.
Appendix: source
Thrown at crates/unicity-aos-bootstrap/src/lib.rs:694
.and_then(|component| match component {
std::path::Component::Normal(name) => Some(name),
_ => None,
})
.is_none()
|| components.next().is_some()
|| relative.extension() != Some(OsStr::new("capsule"))
{
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("embedded capsule source is not canonical: {source}"),
));
}
let asset = relative
.file_name()
.expect("validated capsule source has a filename")
.to_str()
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "capsule asset is not UTF-8")
})?
.to_owned();
if asset != format!("{package}.capsule") {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("embedded capsule source does not match package {package}"),
));
}
if assets.contains(&asset) {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("embedded distro selects duplicate capsule asset {asset}"),
));
}
assets.push(asset);
}
Ok(assets)
}View on GitHub (pinned to f6f22024fb)