astrid-runtime/astrid · error
capsule ' ': distro capsules require a concrete released…
Error message
capsule '{}': distro capsules require a concrete released version or tag What it means
This error comes from distro manifest validation in astrid-cli. A distro capsule with a 'release' selector must pin a concrete released artifact — either a version string or a git tag. If the capsule provides neither (empty version and no tag), the manifest is ambiguous and validate_manifest refuses it.
Solutions
- Set a concrete released version on the capsule's release selector (e.g. version = "1.2.0").
- Or set a tag: instead of version: on the capsule.
- If the capsule should track a dev/source tree, use a path or branch selector instead of a release selector.
- Run validation again to confirm all capsules now pin a release.
Example fix
// before capsules.myapi.version = "" // after capsules.myapi.version = "1.4.2"
Defensive patterns
Strategy: validation
Validate before calling
fn validate_release_selector(version: &str, tag: Option<&str>) -> Result<(), String> {
if version.is_empty() && tag.is_none() {
return Err("release capsule needs a version or tag".into());
}
if tag == Some("") {
return Err("tag must not be empty".into());
}
Ok(())
} Type guard
fn has_release_pin(version: &str, tag: Option<&str>) -> bool {
!version.is_empty() || tag.map(|t| !t.is_empty()).unwrap_or(false)
} Try / catch
match validate_release_selector(&cap.version, cap.tag.as_deref()) {
Ok(()) => build(cap),
Err(e) => eprintln!("capsule '{}': {e}", cap.name),
} Prevention
- Never leave version = "" placeholders in committed manifests.
- CI-lint manifests for empty version fields before build.
- Prefer path/branch selectors for dev capsules so release selectors always carry a pin.
When it happens
Trigger: Calling `astrid distro` validation/build on a manifest where a capsule's release selector has version: "" (empty) and tag: null/absent. Also produced as a side-effect when tests validate capsules whose selectors only use path/branch/rev (those are rejected elsewhere, but a release capsule with neither version nor tag hits this bail).
Common situations: Hand-written distro.toml/manifest where the author left `version = ""` as a placeholder, forgot to fill in the version after copying a capsule block, or intended a branch/dev checkout but left the release selector shape in place.
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
- distro must have at least one capsule with role = "uplink"…
- invites.issuers is non-empty but invites.default-group is…
- invites.max-principals must be "unlimited" or a…
- branding.accent-color
- branding.icon is bytes — distros must not embed assets…
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/59d7e76a5d98268a.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-cli/src/commands/distro/validate.rs:182
"capsule '{}': version '{}' is not valid semver",
cap.name,
cap.version
);
}
let tag = cap.tag.as_deref().map(str::trim);
if let Some(raw_tag) = cap.tag.as_deref()
&& raw_tag != raw_tag.trim()
{
anyhow::bail!(
"capsule '{}': tag must not contain surrounding whitespace",
cap.name
);
}
if matches!(tag, Some("")) {
anyhow::bail!("capsule '{}': tag must not be empty", cap.name);
}
if version.is_empty() && tag.is_none() {
anyhow::bail!(
"capsule '{}': distro capsules require a concrete released version or tag",
cap.name
);
}
}
// At least one uplink.
let has_uplink = manifest
.capsules
.iter()
.any(|c| c.role.as_deref() == Some("uplink"));
if !has_uplink {
anyhow::bail!("distro must have at least one capsule with role = \"uplink\" (a frontend)");
}
// Variable references in capsule env.
let defined_vars: HashSet<&str> = manifest.variables.keys().map(String::as_str).collect();
for cap in &manifest.capsules {View on GitHub (pinned to affd8760f4)