astrid-runtime/astrid · error

capsule '{}': tag must not be empty

Error message

capsule '{}': tag must not be empty

What it means

A capsule may specify a tag, but a tag that is (or trims to) the empty string is meaningless for release resolution, so validate_manifest bails with this error when `tag` is `Some("")` after trimming. This keeps tag-based capsule pins unambiguous.

Source

Thrown at crates/astrid-cli/src/commands/distro/validate.rs:179

        }
        if !version.is_empty() && Version::parse(version).is_err() {
            anyhow::bail!(
                "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)");
    }

View on GitHub (pinned to affd8760f4)

Solutions

  1. Delete the `tag` field entirely if you don't intend to pin a tag (then supply a `version`).
  2. Set the tag to the actual release tag, e.g. `tag = "v1.2.0"`.
  3. Fix the generating script to omit the field when the tag variable is empty.

Example fix

# before
[[capsules]]
name = "http"
tag = ""

# after
[[capsules]]
name = "http"
tag = "v1.2.0"
Defensive patterns

Strategy: validation

Validate before calling

for cap in &manifest.capsules {
    if cap.tag.as_deref().map(str::trim) == Some("") {
        panic!("capsule '{}': tag is empty; remove it or set a real tag", cap.name);
    }
}

Type guard

fn is_meaningful_tag(t: &Option<String>) -> bool {
    !matches!(t.as_deref().map(str::trim), Some(""))
}

Try / catch

if let Err(e) = validate_manifest(&manifest) {
    if e.to_string().contains("tag must not be empty") {
        eprintln!("remove the empty tag or set a real one: {e}");
    }
}

Prevention

When it happens

Trigger: A `[[capsules]]` entry with `tag = ""` (or `tag = " "`, which trims to empty and passes the whitespace check only if raw==trim... — practically, an explicitly empty/blank tag value).

Common situations: A templating/generation script that emits `tag = "${TAG}"` with an unset variable; manually clearing a tag value instead of deleting the field; YAML/TOML quoting an empty string by mistake.

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


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/f32a3e5813c539d6. Report an issue: GitHub.