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

  1. Add a string version field under [package] in Capsule.toml (quote it so TOML keeps it a string) and republish.
  2. If version was written as a number (version = 0.1), change it to a quoted string (version = "0.1.0").
  3. 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

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


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