astrid-runtime/astrid · error

durable capsule metadata names a component absent from its…

Error message

durable capsule {id} metadata names a component absent from its archive

What it means

The manifest has no components at all, yet metadata.wasm_hash is Some. The library throws this because a hash naming an executable implies an executable component exists; with an empty component list the metadata references something that cannot exist in the archive. Verification of the durable package fails before it is served.

Solutions

  1. Add the WASM component back to the manifest/archive and republish.
  2. Set metadata.wasm_hash to None if the capsule is intentionally component-free.
  3. Regenerate both manifest and metadata from the same source tree so they cannot diverge.
  4. Inspect the archive for a manifest that was truncated or mis-authored.

Example fix

// before: empty manifest with metadata hash set
components: [], wasm_hash: Some(hash)
// after: either ship the component
components: vec![Component { path: "component.wasm".into(), .. }], wasm_hash: Some(hash)
// or drop the hash
components: vec![], wasm_hash: None
Defensive patterns

Strategy: validation

Validate before calling

if manifest.components.is_empty() && meta.wasm_hash.is_some() {
    return Err("wasm_hash set but manifest has no components");
}

Type guard

fn has_component_for_hash(manifest: &CapsuleManifest, meta: &CapsuleMeta) -> bool {
    !manifest.components.is_empty() || meta.wasm_hash.is_none()
}

Try / catch

match read_verified_durable_package_for_owner(&store, owner, id).await {
    Ok(pkg) => pkg,
    Err(e) if e.to_string().contains("names a component absent from its archive") => {
        // restore components or clear wasm_hash, then republish
    },
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: read_verified_durable_package_for_owner on a capsule whose manifest.components is empty but whose CapsuleMeta carries a wasm_hash value.

Common situations: A capsule stripped of its components during re-export; metadata copied from another package; manifest generation that dropped the components section while metadata survived.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

        };
        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() {
        bail!("durable capsule {id} metadata names a component absent from its archive");
    }
    let mut effective_capabilities = manifest.capabilities.clone();
    for component in &manifest.components {
        if let Some(capabilities) = &component.capabilities {
            effective_capabilities.merge_from(capabilities);
        }
    }
    if !effective_capabilities
        .expansions_from(&authority.approved_capabilities)
        .is_empty()
    {
        bail!("durable capsule {id} manifest exceeds its authority receipt");
    }
    match verification {
        ArtifactVerification::Signed(provenance) => {
            let signer = provenance.signer.to_string();
            let signature = provenance.signature.to_string();
            if authority.signer.as_deref() != Some(signer.as_str())

View on GitHub (pinned to affd8760f4)