astrid-runtime/astrid · error

capsule source has no Capsule.toml

Error message

capsule source has no Capsule.toml

What it means

Raised by canonical_capsule_archive when the collected entries of the source directory do not include a Capsule.toml at the archive root. Capsule.toml is the mandatory manifest; without it the archive would not be a valid capsule package, so the library refuses to build it.

Solutions

  1. Run the archive/publish command from the directory that directly contains Capsule.toml.
  2. Check the file exists with the exact name/casing `Capsule.toml` (case-sensitive on Linux).
  3. Restore the manifest if deleted, e.g. from version control, then retry the publish.

Example fix

# before: publishing from the wrong directory
publish ./my-capsule/src

# after: publish the capsule root
publish ./my-capsule  # contains Capsule.toml
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_capsule_manifest(dir: &Path) -> Result<(), String> {
    let p = dir.join("Capsule.toml");
    if p.is_file() { Ok(()) } else { Err(format!("missing {}", p.display())) }
}

Type guard

fn is_capsule_root(dir: &Path) -> bool {
    dir.join("Capsule.toml").is_file()
}

Try / catch

if !Path::new("Capsule.toml").exists() {
    eprintln!("run from the capsule root, not {}", env::current_dir()?.display());
    std::process::exit(1);
}

Prevention

When it happens

Trigger: Calling canonical_capsule_archive (directly or via publish_directory_package or canonical_legacy_archive) on a directory that lacks Capsule.toml at its root, or where the manifest was excluded by naming/meta filtering; collect_entries gathered only other files.

Common situations: Running publish from the wrong directory (parent or subdirectory instead of the capsule root); manifest renamed (capsule.toml on case-sensitive FS) or deleted; pointing the publisher at a build-output dir that doesn't contain the manifest.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at crates/astrid-capsule-install/src/storage.rs:751

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()
        .any(|(path, _)| path == Path::new("Capsule.toml"))
    {
        bail!("capsule source has no Capsule.toml");
    }
    entries.sort_by(|left, right| left.0.cmp(&right.0));

    let output = Vec::new();
    let encoder = GzEncoder::new(output, Compression::default());
    let mut builder = Builder::new(encoder);
    builder.mode(tar::HeaderMode::Deterministic);
    for (relative, metadata) in entries {
        append_entry(&mut builder, source_dir, &relative, &metadata)?;
    }
    let encoder = builder
        .into_inner()
        .context("finish canonical capsule tar stream")?;
    encoder
        .finish()
        .context("finish canonical capsule gzip stream")
}

View on GitHub (pinned to affd8760f4)