unicity-aos/aos-ce · error · std::io::Error
embedded capsule has no source
Error message
embedded capsule has no source
What it means
For each `[[capsule]]` entry, capsule_assets_from_manifest reads the `source` field as a string, which gives the path of the capsule asset relative to the distro. This error is thrown when an entry lacks `source` or the value is not a string. Without it the code cannot locate or validate the embedded asset.
Solutions
- Add a string `source` field to the offending `[[capsule]]` entry, e.g. `source = "capsules/<package>.capsule"`.
- Ensure the value is a single TOML string, not an array or table.
- Make the source path canonical (see error 18): exactly `capsules/<name>.capsule` relative to the distro root.
- Verify with the manifest-producing build script that every capsule entry carries both name and source.
Example fix
// before [[capsule]] name = "foo" // after [[capsule]] name = "foo" source = "capsules/foo.capsule"
Defensive patterns
Strategy: validation
Validate before calling
// Rust: check every capsule entry has a string source before calling the API
fn ensure_capsule_sources(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("source").and_then(toml::Value::as_str).is_none() {
return Err(format!("capsule entry #{i} is missing a string `source`"));
}
}
Ok(())
} Type guard
fn capsule_source(capsule: &toml::Value) -> Option<&str> {
capsule.get("source").and_then(toml::Value::as_str)
} Try / catch
match prepare_unicity_ce_init(...) {
Err(e) if e.to_string().contains("embedded capsule has no source") => {
eprintln!("a [[capsule]] entry lacks a string `source`; add `source = \"capsules/<pkg>.capsule\"`");
}
other => other,
} Prevention
- Use a serde struct with required `source: String` for manifest parsing so omissions surface early.
- Template new [[capsule]] entries with both name and source filled in.
- Add a build-time test asserting every capsule entry has name and source.
When it happens
Trigger: A `[[capsule]]` entry in UNICITY_CE_MANIFEST omits `source`, or `source` is a non-string TOML value. Raised by capsule_assets_from_manifest when called via capsule_dir_with, prepare_unicity_ce_init, install_capsule_fixtures, or the serialization test.
Common situations: Adding a new capsule entry and forgetting the source path; a manifest generator emitting only `name`; renaming `source` to something like `path` during a refactor; typing `source` as an array of paths for multi-asset capsules.
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
- embedded capsule has no name
- embedded distro has no capsules
- embedded capsule source has no asset
- [package].name is missing or empty.
- Subscribe ` ` has priority outside the u32 range.
AI-assisted analysis of unicity-aos/aos-ce@f6f22024fb (2026-09-13).
Data as JSON: /api/errors/38128db3d488034e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/unicity-aos-bootstrap/src/lib.rs:669
.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()
|| relative.extension() != Some(OsStr::new("capsule"))
{
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("embedded capsule source is not canonical: {source}"),
));View on GitHub (pinned to f6f22024fb)