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

  1. Set a concrete released version on the capsule's release selector (e.g. version = "1.2.0").
  2. Or set a tag: instead of version: on the capsule.
  3. If the capsule should track a dev/source tree, use a path or branch selector instead of a release selector.
  4. 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

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


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)