{"record":{"id":"38128db3d488034e","repo":"unicity-aos/aos-ce","slug":"embedded-capsule-has-no-source","errorCode":null,"errorMessage":"embedded capsule has no source","messagePattern":"embedded capsule has no source","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/unicity-aos-bootstrap/src/lib.rs","lineNumber":669,"sourceCode":"        .ok_or_else(|| {\n            io::Error::new(\n                io::ErrorKind::InvalidData,\n                \"embedded distro has no capsules\",\n            )\n        })?;\n    let mut assets = Vec::with_capacity(capsules.len());\n    for capsule in capsules {\n        let package = capsule\n            .get(\"name\")\n            .and_then(toml::Value::as_str)\n            .ok_or_else(|| {\n                io::Error::new(io::ErrorKind::InvalidData, \"embedded capsule has no name\")\n            })?;\n        let source = capsule\n            .get(\"source\")\n            .and_then(toml::Value::as_str)\n            .ok_or_else(|| {\n                io::Error::new(io::ErrorKind::InvalidData, \"embedded capsule has no source\")\n            })?;\n        let relative = Path::new(source);\n        let mut components = relative.components();\n        if components.next() != Some(std::path::Component::Normal(OsStr::new(\"capsules\")))\n            || components\n                .next()\n                .and_then(|component| match component {\n                    std::path::Component::Normal(name) => Some(name),\n                    _ => None,\n                })\n                .is_none()\n            || components.next().is_some()\n            || relative.extension() != Some(OsStr::new(\"capsule\"))\n        {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\"embedded capsule source is not canonical: {source}\"),\n            ));","sourceCodeStart":651,"sourceCodeEnd":687,"githubUrl":"https://github.com/unicity-aos/aos-ce/blob/f6f22024fb1e8d122f28a1b4a9f75aee448ae839/crates/unicity-aos-bootstrap/src/lib.rs#L651-L687","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n[[capsule]]\nname = \"foo\"\n\n// after\n[[capsule]]\nname = \"foo\"\nsource = \"capsules/foo.capsule\"","handlingStrategy":"validation","validationCode":"// Rust: check every capsule entry has a string source before calling the API\nfn ensure_capsule_sources(manifest_toml: &str) -> Result<(), String> {\n    let v: toml::Value = toml::from_str(manifest_toml).map_err(|e| e.to_string())?;\n    for (i, c) in v.get(\"capsule\").and_then(toml::Value::as_array).unwrap_or(&vec![]).iter().enumerate() {\n        if c.get(\"source\").and_then(toml::Value::as_str).is_none() {\n            return Err(format!(\"capsule entry #{i} is missing a string `source`\"));\n        }\n    }\n    Ok(())\n}","typeGuard":"fn capsule_source(capsule: &toml::Value) -> Option<&str> {\n    capsule.get(\"source\").and_then(toml::Value::as_str)\n}","tryCatchPattern":"match prepare_unicity_ce_init(...) {\n    Err(e) if e.to_string().contains(\"embedded capsule has no source\") => {\n        eprintln!(\"a [[capsule]] entry lacks a string `source`; add `source = \\\"capsules/<pkg>.capsule\\\"`\");\n    }\n    other => other,\n}","preventionTips":["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."],"tags":["toml","manifest","missing-field","capsule"],"backgroundTag":"missing-required-config-field","analyzedSha":"f6f22024fb1e8d122f28a1b4a9f75aee448ae839","analyzedAt":"2026-09-13T03:04:44.565Z","contentChangedAt":"2026-09-13T03:04:44.565Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}