astrid-runtime/astrid · error
Capsule.toml has no package.version
Error message
Capsule.toml has no package.version
What it means
manifest_identity also reads package.version from Capsule.toml to establish the capsule identity. When the [package] table exists but has no string version field, the library throws because a publishable capsule must be versioned.
Source
Thrown at crates/astrid-capsule-install/src/storage.rs:730
}
}
Ok(())
}
fn manifest_identity(source_dir: &Path) -> anyhow::Result<(String, String)> {
let manifest = fs::read_to_string(source_dir.join("Capsule.toml"))
.with_context(|| format!("read capsule manifest from {}", source_dir.display()))?;
let value: toml::Value = toml::from_str(&manifest).context("parse capsule manifest")?;
let id = value
.get("package")
.and_then(|package| package.get("name"))
.and_then(toml::Value::as_str)
.ok_or_else(|| anyhow::anyhow!("Capsule.toml has no package.name"))?;
let version = value
.get("package")
.and_then(|package| package.get("version"))
.and_then(toml::Value::as_str)
.ok_or_else(|| anyhow::anyhow!("Capsule.toml has no package.version"))?;
Ok((id.to_owned(), version.to_owned()))
}
fn authority_capsule_id(authority: &[u8]) -> anyhow::Result<String> {
let authority: InstalledAuthority =
serde_json::from_slice(authority).context("decode authority receipt for package id")?;
if authority.capsule_id.is_empty() {
bail!("authority receipt has an empty capsule id");
}
Ok(authority.capsule_id)
}
/// Build a deterministic gzip/tar package from a checked directory.
pub fn canonical_capsule_archive(source_dir: &Path) -> anyhow::Result<Vec<u8>> {
let mut entries = Vec::new();
collect_entries(source_dir, source_dir, &mut entries)?;
if !entries
.iter()View on GitHub (pinned to affd8760f4)
Solutions
- Add a string version field under [package] in Capsule.toml (quote it so TOML keeps it a string) and republish.
- If version was written as a number (version = 0.1), change it to a quoted string (version = "0.1.0").
- Regenerate Capsule.toml with the current scaffolding tool to get a complete manifest.
Example fix
// before // version = 0.1 // after // [package] // name = "my-capsule" // version = "0.1.0"
Defensive patterns
Strategy: validation
Validate before calling
fn manifest_has_version(capsule_dir: &Path) -> anyhow::Result<()> {
let raw = std::fs::read_to_string(capsule_dir.join("Capsule.toml"))?;
let v: toml::Value = toml::from_str(&raw)?;
if v.get("package").and_then(|p| p.get("version")).and_then(|n| n.as_str()).is_some() {
Ok(())
} else {
Err(anyhow::anyhow!("Capsule.toml missing [package].version string"))
}
} Prevention
- Quote the version value in Capsule.toml so TOML parses it as a string, not a float
- Add version to every new capsule template
- Validate the full [package] table (name and version) in CI before publish
When it happens
Trigger: publish_directory_package parses Capsule.toml successfully and finds package.name, but value.get("package").get("version").as_str() is None — the version key is absent or is not a string (e.g. a float like 0.1).
Common situations: Newly initialized capsule projects where the developer added name but not version; version written unquoted as a TOML float (version = 1.0); manifests edited by tooling that strips version.
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
- Capsule.toml has no package.name
- capsule archive entry '{requested}' is not a regular file
- existing [security.capsule_local_egress].{capsule_id} is not
- distro.version '{}' is not valid semver
- Cargo include table must contain a string path in {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/2625a8e52db3e20e.
Report an issue: GitHub.