astrid-runtime/astrid · error
durable capsule {id} exports differ between metadata and arc
Error message
durable capsule {id} exports differ between metadata and archive What it means
During durable package verification, verify_package_identity recomputes the export list from the manifest's WIT exports (name + version) and compares it with the exports recorded in the capsule metadata. This bail fires when the two differ, meaning the metadata file was hand-edited, regenerated from a different manifest, or paired with the wrong archive. The library throws it to prevent installing a capsule whose advertised API surface does not match what was actually approved and archived.
Source
Thrown at crates/astrid-capsule-install/src/storage.rs:289
}
let manifest_digest = crate::authority::digest_manifest(manifest_bytes);
if authority.manifest_digest != manifest_digest {
bail!("durable capsule {id} manifest digest differs from authority receipt");
}
if authority.content_digest != verification.content_digest() {
bail!("durable capsule {id} content digest differs from authority receipt");
}
let expected_imports = crate::wit::version_map_to_strings(&manifest.imports, |definition| {
definition.version.to_string()
});
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())View on GitHub (pinned to affd8760f4)
Solutions
- Regenerate the capsule metadata (CapsuleMeta) from the current manifest/archive so the exports field matches manifest.exports versions.
- Reinstall the durable capsule from a freshly built, internally consistent package so metadata, manifest, and authority receipt are all produced together.
- If the old exports are intended, restore the original manifest/archive that matches the existing metadata instead of changing metadata.
- Compare manifest.exports vs metadata.exports (names and version strings) to identify which side is stale before regenerating.
Example fix
// before: metadata written once, manifest later gained an export let metadata = read_metadata(); // exports: ["astrid:store@1.0.0"] let manifest = read_manifest(); // exports: ["astrid:store@1.1.0"] // after: regenerate metadata from the manifest before installing let mut metadata = read_metadata(); metadata.exports = crate::wit::version_map_to_strings(&manifest.exports, |d| d.version.to_string()); write_metadata(&metadata);
Defensive patterns
Strategy: validation
Validate before calling
let expected_exports = crate::wit::version_map_to_strings(&manifest.exports, |d| d.version.to_string());
if metadata.exports != expected_exports {
return Err(anyhow!("exports mismatch: metadata {:?} vs manifest {:?}", metadata.exports, expected_exports));
} Prevention
- Always generate metadata and manifest together from a single build step.
- Never hand-edit metadata exports; regenerate them from the manifest.
- Add a packaging CI check that recomputes exports from the manifest and diffs against metadata.
When it happens
Trigger: Calling read_verified_durable_package_for_owner (or the test-path durable_metadata_cross_binding_rejects_manifest_and_archive_mismatches) on a durable capsule where metadata.exports != version_map_to_strings(manifest.exports), i.e. the manifest in the archive declares exports (names or versions) that don't exactly equal the exports list persisted in CapsuleMeta.
Common situations: Rebuilding a capsule with added/removed/renamed WIT exports but reusing the old metadata file; bumping an export's version in the manifest without regenerating metadata; mixing archives and metadata from different capsule versions; editing metadata by hand or with a tool that rewrote the exports field.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- durable capsule {id} metadata executable hash differs from a
- durable capsule {id} WASM hash differs between authority and
- cannot remove capsule authority while an install transaction
- an incomplete capsule authority update exists at {}; remove
- unsupported installed authority schema {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/b71866d0f3768bde.
Report an issue: GitHub.