astrid-runtime/astrid · error

capsule name '{}' is invalid (must match ^[a-z][a-z0-9-]*$)

Error message

capsule name '{}' is invalid (must match ^[a-z][a-z0-9-]*$)

What it means

Capsule names become filesystem path components in the `.shuttle` layout (`capsules/<name>.capsule`) and under the capsule store, so validate_manifest enforces the safe identifier pattern `^[a-z][a-z0-9-]*$`. This blocks `/`, `..`, uppercase, underscores, and other path-hostile or ambiguous characters from reaching the disk layout.

Source

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

                );
            }
        }
    }

    // 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);
        }
        // Distros compose *released* capsules. `branch`/`rev` selectors
        // can only be honored by compiling from a git ref — the exact
        // toolchain dependency offline/headless distro seeding exists to
        // remove. Reject them: a distro must pin a released `version` or
        // `tag`. (Git-ref installs remain available via the standalone
        // `astrid capsule install` command.)
        if cap.branch.is_some() || cap.rev.is_some() {
            anyhow::bail!(
                "capsule '{}': branch/rev require building from source and are not allowed in a \
                 distro manifest — pin a released `version` or `tag` (git-ref installs are \
                 available via `astrid capsule install`).",

View on GitHub (pinned to affd8760f4)

Solutions

  1. Rename the capsule to lowercase letters/digits/hyphens starting with a letter (e.g. `my-lib`).
  2. Update all references to the old capsule name in requires/selectors.
  3. Re-run distro validation to confirm the new name passes.

Example fix

# before
[[capsules]]
name = "My_Lib/2"

# after
[[capsules]]
name = "my-lib-2"
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_id(name: &str) -> bool {
    let mut chars = name.chars();
    matches!(chars.next(), Some(c) if c.is_ascii_lowercase())
        && chars.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
}
for cap in &manifest.capsules {
    assert!(is_valid_id(&cap.name), "invalid capsule name: {}", cap.name);
}

Type guard

fn is_valid_capsule_name(s: &str) -> bool {
    !s.is_empty()
        && s.chars().next().is_some_and(|c| c.is_ascii_lowercase())
        && s.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
}

Try / catch

if let Err(e) = validate_manifest(&manifest) {
    if e.to_string().contains("is invalid (must match") {
        eprintln!("rename capsule: {e}");
    }
}

Prevention

When it happens

Trigger: A `[[capsules]]` entry whose `name` fails `is_valid_id`: starts with a digit or `-`, contains uppercase, `_`, `/`, `.`, `..`, spaces, or is empty.

Common situations: Naming a capsule after a GitHub repo (`My-Lib_v2`); pasting a path like `tools/http`; typos such as leading whitespace or a leading digit `2fa`.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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