astrid-runtime/astrid · error
capsule '{}': version '{}' is not valid semver
Error message
capsule '{}': version '{}' is not valid semver What it means
After rejecting whitespace, validate_manifest parses the capsule's version with `semver::Version::parse` (a concrete version, not a requirement) and bails if it is not valid semver. Empty versions are skipped here because a separate check requires either version or tag.
Source
Thrown at crates/astrid-cli/src/commands/distro/validate.rs:163
// `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`).",
cap.name,
);
}
let version = cap.version.trim();
if cap.version != version {
anyhow::bail!(
"capsule '{}': version must not contain surrounding whitespace",
cap.name
);
}
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() {View on GitHub (pinned to affd8760f4)
Solutions
- Rewrite the version as full semver `MAJOR.MINOR.PATCH` (optionally with prerelease/build, e.g. `1.2.0-rc.1`).
- Remove a leading `v` — `v1.2.0` belongs in `tag`, not `version`.
- If you meant a git tag, set `tag = "v1.2.0"` and clear `version` (or keep both consistent).
Example fix
# before [[capsules]] name = "http" version = "v1.2" # after [[capsules]] name = "http" version = "1.2.0"
Defensive patterns
Strategy: validation
Validate before calling
for cap in &manifest.capsules {
let v = cap.version.trim();
if !v.is_empty() && semver::Version::parse(v).is_err() {
panic!("capsule '{}': version '{}' is not valid semver", cap.name, cap.version);
}
} Type guard
fn is_exact_semver(s: &str) -> bool {
semver::Version::parse(s.trim()).is_ok()
} Try / catch
if let Err(e) = validate_manifest(&manifest) {
if e.to_string().contains("is not valid semver") {
eprintln!("use MAJOR.MINOR.PATCH: {e}");
}
} Prevention
- Always write three-component versions (1.2.0, not 1.2 or v1.2)
- Put git tag strings (with leading v) in `tag`, not `version`
- Parse-check versions with a semver library in CI
When it happens
Trigger: A `[[capsules]]` entry with a non-empty `version` like `"1.2"`, `"v1.2.0"`, `"1.2.0-beta"` is actually valid — invalid ones are `"1.2"`, `"v1.0.0"`, `"latest"`, `"1.2.3.4"`, or date strings.
Common situations: Using a two-component version (`1.2`) where semver demands three; prefixing with `v` like a git tag; putting a tag name or `latest` into the version field instead of the `tag` field.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- distro.astrid-version '{av}' is not a valid semver requireme
- distro.requires.{ns}.{name} '{req}' is not a valid semver re
- distro.version '{}' is not valid semver
- distro must contain at least one capsule
- capsule name '{}' is invalid (must match ^[a-z][a-z0-9-]*$)
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/f07df53e560b9641.
Report an issue: GitHub.