astrid-runtime/astrid · error

durable capsule component is missing from its archive

Error message

durable capsule {id} component is missing from its archive

What it means

verify_package_identity looks up the first manifest component's relative path in the archive's file map (archive_files) to hash and verify it. This bail fires when no entry with that exact relative path exists in the archive, i.e. the manifest references a component file that is not actually packaged. The library throws it because the declared executable cannot be verified or executed if it is absent from the archive.

Solutions

  1. Repack the archive so it contains the file at exactly the path given by manifest.components[0].path.
  2. Fix the manifest's component path to match the actual archive entry name (paths are case- and prefix-sensitive).
  3. Reinstall the capsule from a complete, correctly packaged build instead of a partially assembled archive.
  4. List archive entries and diff them against manifest component paths to find the missing/renamed file.

Example fix

// before: manifest points at "bin/component.wasm" but archive only has "component.wasm"
// after: align the path when packaging
let relative = "component.wasm";
manifest.components[0].path = PathBuf::from(relative);
archive_files.insert(relative.to_string(), wasm_bytes);
Defensive patterns

Strategy: validation

Validate before calling

for component in &manifest.components {
    let key = component.path.to_str().ok_or_else(|| anyhow!("non-UTF-8 path"))?;
    anyhow::ensure!(archive_files.contains_key(key), "archive missing component {key}");
}

Prevention

When it happens

Trigger: Calling read_verified_durable_package_for_owner on a capsule where manifest.components[0].path (as a UTF-8 string) is not a key in the archive's BTreeMap of files — the manifest was edited/renamed without repacking, or the archive was built without including the component.

Common situations: Renaming the component file or its directory in the manifest but shipping the old archive; a packaging script that skips the wasm file (e.g. .gitignore or build output filtering); mismatched manifest and archive versions extracted from different builds; typos in the component path (case-sensitive lookup).

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/d88d83ae7935d3df. Report an issue: GitHub.

Appendix: source

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

    });
    if metadata.imports != expected_imports {
        bail!("durable capsule {id} imports differ between metadata and archive");
    }
    let expected_exports = crate::wit::version_map_to_strings(&manifest.exports, |definition| {
        definition.version.to_string()
    });
    if metadata.exports != expected_exports {
        bail!("durable capsule {id} exports differ between metadata and archive");
    }
    if authority.wasm_hash_pinned && metadata.wasm_hash != authority.approved_wasm_hash {
        bail!("durable capsule {id} metadata executable hash differs from authority receipt");
    }
    if let Some(component) = manifest.components.first() {
        let Some(relative) = component.path.to_str() else {
            bail!("durable capsule {id} component path is not UTF-8");
        };
        let Some(bytes) = archive_files.get(relative) else {
            bail!("durable capsule {id} component is missing from its archive");
        };
        if Path::new(relative)
            .extension()
            .is_some_and(|extension| extension.eq_ignore_ascii_case("wasm"))
        {
            let archive_hash = blake3::hash(bytes).to_hex().to_string();
            if authority.wasm_hash_pinned
                && authority.approved_wasm_hash.as_deref() != Some(archive_hash.as_str())
            {
                bail!("durable capsule {id} WASM hash differs between authority and archive");
            }
            if metadata.wasm_hash.as_deref() != Some(archive_hash.as_str()) {
                bail!("durable capsule {id} WASM hash differs between metadata and archive");
            }
        } else if metadata.wasm_hash.is_some() {
            bail!("durable capsule {id} metadata names a hash for a non-WASM component");
        }
    } else if metadata.wasm_hash.is_some() {

View on GitHub (pinned to affd8760f4)