astrid-runtime/astrid · error
distro provenance receipt read-back differs
Error message
distro provenance receipt read-back differs
What it means
After writing a distro provenance receipt during migration, verify_destination reads the value back from the destination store, hashes it with blake3, and compares to the expected digest. On mismatch it returns AlreadyExists with 'distro provenance receipt read-back differs', indicating the persisted receipt does not match what was intended to be written.
Solutions
- Re-run the migration for this entry after quiescing other writers to the store
- Verify backend/store integrity; check for stale caches or replication lag in the destination store
- Delete the divergent receipt key and re-migrate so it is rewritten atomically
- Enable single-writer semantics or locking around migration to prevent concurrent overwrites
Example fix
// before: concurrent job rewriting the same key 0 * * * * reconcile-store --all # races with migration // after: pause conflicting jobs during migration systemctl stop reconcile.timer && migrate && systemctl start reconcile.timer
Defensive patterns
Strategy: try-catch
Validate before calling
let bytes = dest.get(KEY).await?;
if let Some(b) = bytes {
if format!("blake3:{}", blake3::hash(&b).to_hex()) != expected_digest {
eprintln!("receipt diverges before migration completes");
}
} Try / catch
match migrate_one(entry).await {
Err(e) if e.to_string().contains("read-back differs") => {
// quiesce writers, delete key, retry migration
}
other => other?,
} Prevention
- Ensure single-writer access during migration
- Back up the destination store before migrating
- Verify backend consistency (replication lag, caches)
- Make receipt writes atomic and idempotent
When it happens
Trigger: Running migrate_one → verify_destination when the read-back bytes' blake3 hash differs from the expected digest recorded during the write phase.
Common situations: Concurrent writers overwrote the receipt mid-migration; storage corruption or a lying/network store returning stale or different bytes; non-atomic store writes; retried migration overwriting the key with different content.
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 contracts blob digest mismatch
- durable capsule content digest differs from authority…
- durable capsule failed authoritative verification
- durable capsule failed byte-for-byte readback
- durable capsule manifest digest differs from authority…
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/413c87f8ab928f39.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-kernel/src/principal_distro_migration.rs:306
retire_source(&source, &receipt.source_digest, receipt.source_bytes)
}
async fn verify_destination(
store: &RuntimePrincipalStore,
uid: PrincipalUid,
expected_digest: &str,
) -> io::Result<()> {
let scoped = store
.principal_control_kv(uid, "distro")
.map_err(io::Error::other)?;
let bytes = scoped
.get(KEY)
.await
.map_err(io::Error::other)?
.ok_or_else(|| io::Error::other("distro provenance is missing after receipt"))?;
let actual = format!("blake3:{}", blake3::hash(&bytes).to_hex());
if actual != expected_digest {
return Err(io::Error::new(
io::ErrorKind::AlreadyExists,
"distro provenance receipt read-back differs",
));
}
Ok(())
}
fn convert_lock(lock: LegacyLock) -> io::Result<DistroProvenance> {
let provenance = DistroProvenance {
schema_version: lock.schema_version,
distro_id: lock.distro.id,
distro_version: lock.distro.version,
resolved_at: lock.distro.resolved_at,
capsules: lock
.capsules
.into_iter()
.map(|capsule| DistroCapsuleProvenance {
name: capsule.name,View on GitHub (pinned to affd8760f4)