astrid-runtime/astrid · error
durable capsule {id} WIT digest mismatch for {relative}
Error message
durable capsule {id} WIT digest mismatch for {relative} What it means
Each WIT file pinned in capsule metadata carries a blake3 hex digest ('pin'). verify_wit_files hashes the actual bytes of `wit/<relative>` from the archive and compares, also requiring the pin to be a 64-char lowercase hex string. A mismatch means the archive's WIT content differs from what the metadata pins — tampering, corruption, or stale metadata — so the durable package is rejected.
Source
Thrown at crates/astrid-capsule-install/src/storage.rs:228
if relative_path.is_absolute()
|| relative_path.components().any(|component| {
matches!(
component,
std::path::Component::ParentDir | std::path::Component::RootDir
)
})
|| relative_path
.components()
.any(|component| !matches!(component, std::path::Component::Normal(_)))
{
bail!("durable capsule {id} has unsafe WIT metadata path {relative}");
}
let key = format!("wit/{relative}");
let Some(bytes) = files.get(&key) else {
bail!("durable capsule {id} is missing WIT file {relative}");
};
if !is_hex_digest(pin) || blake3::hash(bytes).to_hex().as_str() != pin {
bail!("durable capsule {id} WIT digest mismatch for {relative}");
}
expected.insert(key);
}
for key in files.keys().filter(|key| key.starts_with("wit/")) {
if Path::new(key)
.extension()
.is_some_and(|extension| extension.eq_ignore_ascii_case("wit"))
&& !expected.contains(key)
{
bail!("durable capsule {id} has an unpinned WIT file {key}");
}
}
Ok(())
}
fn is_hex_digest(value: &str) -> bool {
value.len() == 64
&& valueView on GitHub (pinned to affd8760f4)
Solutions
- Regenerate the capsule package so metadata pins are recomputed from the current .wit files, then republish/reinstall.
- Check the pin value is a 64-character lowercase hex blake3 digest and correct typos.
- If files were hand-edited inside the durable store, rebuild the capsule from source instead.
- Compare `blake3 hash wit/<file>` output against the metadata pin to identify which file drifted.
Example fix
// before: stale pin after editing the wit file
wit_files: {"api.wit": "<old-hash>"}
// after: regenerate pins from current content
wit_files: {"api.wit": blake3_hex(read("wit/api.wit"))} Defensive patterns
Strategy: validation
Validate before calling
fn wit_pins_match(wit_files: &[(String, String)], files: &std::collections::BTreeMap<String, Vec<u8>>) -> bool {
wit_files.iter().all(|(rel, pin)| {
pin.len() == 64 && pin.bytes().all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))
&& files.get(&format!("wit/{rel}")).map(|bytes| blake3::hash(bytes).to_hex().as_str() == pin.as_str()).unwrap_or(false)
})
} Type guard
fn is_hex_digest(value: &str) -> bool {
value.len() == 64 && value.bytes().all(|b| b.is_ascii_digit() || matches!(b, b'a'..=b'f'))
} Try / catch
match read_verified_durable_package_for_owner(id) {
Err(e) if e.to_string().contains("WIT digest mismatch") => {
// content drifted from pinned metadata: rebuild and republish the capsule
rebuild_and_republish(id)?;
}
other => other?,
} Prevention
- Always regenerate metadata pins after modifying any .wit file
- Never copy pins between files or hand-write digest values
- Compare `blake3 hash` output to metadata when debugging drift
- Rebuild capsules from one source of truth rather than patching installed files
When it happens
Trigger: read_verified_durable_package_for_owner where metadata.wit_files contains an invalid pin (not 64 hex chars) or the blake3 hash of the archive's wit file bytes differs from the pin — e.g. the .wit file was modified after metadata generation, or the wrong pin was recorded.
Common situations: Editing a .wit file and re-archiving without regenerating metadata pins; copy-paste of pins between files; corrupted archive transfer; mixing files from two capsule versions.
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 {} is missing its pinned contracts blob
- durable capsule {id} is missing WIT file {relative}
- durable capsule {id} has an unpinned WIT file {key}
- durable capsule {id} imports differ between metadata and arc
- durable capsule {} contracts blob digest mismatch
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/ee2efb18bf105f94.
Report an issue: GitHub.