astrid-runtime/astrid · critical
capsule ' ' hash mismatch: lock has , archive has
Error message
capsule '{}' hash mismatch: lock has {}, archive has {actual} What it means
For each locked capsule, verify_capsule_hashes reads the mirrored archive and computes its blake3 hash, comparing it to the hash recorded in the lock. A mismatch means the archive on the shuttle differs from what was sealed — corruption or substitution. The install aborts before any side effects.
Solutions
- Re-copy or rebuild the shuttle so each capsule archive matches its locked blake3 hash
- Check the source media/storage for corruption and re-transfer
- Never replace archive files on a sealed shuttle; repack instead
Defensive patterns
Strategy: validation
Validate before calling
let bytes = std::fs::read(&file)?;
let actual = format!("blake3:{}", blake3::hash(&bytes).to_hex());
if entry.hash != actual {
return Err(anyhow!("capsule '{}' hash mismatch", entry.name));
} Prevention
- Never modify archives on a sealed shuttle; repack instead
- Use verified transfers for removable media
- Re-check hashes after any re-copy of the shuttle
When it happens
Trigger: install_from_shuttle where a mirrored capsule file's blake3 hash differs from lock.capsules[].hash — bit rot on removable media, truncated copy, or a replaced archive file.
Common situations: USB media corruption or incomplete copy; someone swapped an archive on the shuttle; shuttle contents edited after sealing.
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
- manifest hash mismatch: lock records
- BLAKE3 evidence collision with inconsistent file lengths
- BLAKE3 evidence collision with inconsistent lengths
- durable capsule contracts blob digest mismatch
- durable capsule WASM hash differs between authority and…
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/aa521a1d869bf282.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-cli/src/commands/distro/shuttle_install.rs:311
/// Verify the per-capsule blake3 of every lock entry against the bytes
/// actually present in the mirror. Returns an error on the first
/// mismatch or missing file. Pure (no install side effects) so the
/// integrity gate is unit-testable.
fn verify_capsule_hashes(mirror: &Path, lock: &DistroLock) -> anyhow::Result<()> {
for entry in &lock.capsules {
let file = shuttle::capsule_mirror_path(mirror, &entry.name);
if !file.is_file() {
bail!(
"capsule '{}' is missing from the shuttle mirror",
entry.name
);
}
let bytes = std::fs::read(&file)
.with_context(|| format!("failed to read mirrored capsule {}", entry.name))?;
let actual = format!("blake3:{}", blake3::hash(&bytes).to_hex());
if entry.hash != actual {
bail!(
"capsule '{}' hash mismatch: lock has {}, archive has {actual}",
entry.name,
entry.hash
);
}
}
Ok(())
}
fn source_digest(path: &Path) -> anyhow::Result<String> {
let mut file = std::fs::File::open(path)
.with_context(|| format!("open capsule source {}", path.display()))?;
let mut hasher = blake3::Hasher::new();
let mut buffer = vec![0_u8; 1024 * 1024].into_boxed_slice();
loop {
let count = file.read(&mut buffer)?;
if count == 0 {
break;View on GitHub (pinned to affd8760f4)