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
- Run the archive/publish command from the directory that directly contains Capsule.toml.
- Check the file exists with the exact name/casing `Capsule.toml` (case-sensitive on Linux).
- 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
- Always publish from the directory containing Capsule.toml
- Watch the exact casing `Capsule.toml` on case-sensitive filesystems
- Commit Capsule.toml so it can't be silently deleted
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
- an incomplete capsule authority update exists at
- astrid-build failed with exit code
- astrid-build produced no .capsule archive.
- authority receipt has an empty capsule id
- candidate generation for
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)