astrid-runtime/astrid · error
distro must contain at least one capsule
Error message
distro must contain at least one capsule
What it means
A distro is, by definition, a bundle of one or more released capsules, so validate_manifest bails when `manifest.capsules` is empty. This prevents emitting a degenerate distro artifact that would install nothing. It fires after the astrid-version and requires checks in the same validation pass.
Source
Thrown at crates/astrid-cli/src/commands/distro/validate.rs:122
&& semver::VersionReq::parse(av).is_err()
{
anyhow::bail!("distro.astrid-version '{av}' is not a valid semver requirement");
}
// Requires version strings are valid semver requirements.
for (ns, ifaces) in &manifest.distro.requires {
for (name, req) in ifaces {
if semver::VersionReq::parse(req).is_err() {
anyhow::bail!(
"distro.requires.{ns}.{name} '{req}' is not a valid semver requirement",
);
}
}
}
// At least one capsule.
if manifest.capsules.is_empty() {
anyhow::bail!("distro must contain at least one capsule");
}
// No duplicate capsule names, and each name must be a valid
// identifier. Names become path components in the `.shuttle` layout
// (`capsules/<name>.capsule`) and on disk under the capsule store;
// constraining them to `^[a-z][a-z0-9-]*$` keeps a manifest from
// introducing `/`, `..`, or other path-hostile characters there.
let mut seen_names = HashSet::new();
for cap in &manifest.capsules {
if !is_valid_id(&cap.name) {
anyhow::bail!(
"capsule name '{}' is invalid (must match ^[a-z][a-z0-9-]*$)",
cap.name,
);
}
if !seen_names.insert(&cap.name) {
anyhow::bail!("duplicate capsule name '{}'", cap.name);
}View on GitHub (pinned to affd8760f4)
Solutions
- Add at least one `[[capsules]]` entry pinning a released `version` or `tag`.
- Fix the script/template that generated the manifest so it emits capsule entries.
- If you truly want no capsules, don't build a distro — use `astrid capsule install` directly.
Example fix
# before [distro] name = "my-distro" # no capsules # after [distro] name = "my-distro" [[capsules]] name = "http" version = "1.2.0"
Defensive patterns
Strategy: validation
Validate before calling
if manifest.capsules.is_empty() {
panic!("distro manifest has no capsules; add at least one [[capsules]] entry");
} Try / catch
match validate_manifest(&manifest) {
Err(e) if e.to_string().contains("at least one capsule") => {
eprintln!("manifest is empty of capsules — add [[capsules]] entries");
}
other => other?,
} Prevention
- Never delete all [[capsules]] sections when editing distro manifests
- Check generator output for zero capsules before invoking the CLI
- Keep a minimal working capsule entry in scaffold templates
When it happens
Trigger: Running the distro build/validation with a manifest that declares `distro` metadata but has an empty `capsules` list — e.g. no `[[capsules]]` sections, or all capsule sections removed/commented out.
Common situations: New distro manifests scaffolded empty; bulk-deleting capsule entries while debugging; a generation script that produced no capsules due to an upstream filter.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- distro.astrid-version '{av}' is not a valid semver requireme
- distro.requires.{ns}.{name} '{req}' is not a valid semver re
- capsule name '{}' is invalid (must match ^[a-z][a-z0-9-]*$)
- duplicate capsule name '{}'
- capsule '{}': branch/rev require building from source and ar
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/54c1c14d0461efe9.
Report an issue: GitHub.