FuelLabs/fuel-core · critical
Registry root mismatch. registry root after decompression: {
Error message
Registry root mismatch. registry root after decompression: {:?}, registry root after compression: {:?} What it means
With fault-proving enabled, decompression recomputes the registry root over the decompression-side registry and compares it with registry_root stored in the compressed block header (V1); any difference aborts with 'Registry root mismatch…' (crates/compression/src/decompress.rs:126). This is the fault-proving integrity check firing: the registry content used to decompress differs from the content used to compress — e.g. pruned/missing registrations, divergent temporal registry retention, or the wrong DB. It signals real state divergence, not a transient failure.
Source
Thrown at crates/compression/src/decompress.rs:126
#[allow(clippy::arithmetic_side_effects)]
u16::try_from(transaction_count - 1)?,
);
} else {
anyhow::bail!("Last transaction is not a mint");
}
#[cfg(feature = "fault-proving")]
{
match block {
VersionedCompressedBlock::V0(_) => {}
VersionedCompressedBlock::V1(ref block) => {
let registry_root_after_decompression = ctx
.db
.registry_root()
.map_err(|e| anyhow::anyhow!("Failed to get registry root: {}", e))?;
let registry_root_after_compression = block.header.registry_root;
if registry_root_after_decompression != registry_root_after_compression {
anyhow::bail!(
"Registry root mismatch. registry root after decompression: {:?}, registry root after compression: {:?}",
registry_root_after_decompression,
registry_root_after_compression
);
}
}
}
}
Ok(PartialFuelBlock {
header: block.partial_block_header(),
transactions,
})
}
pub struct DecompressCtx<D> {
pub config: Config,
/// Timestamp of the block being decompressedView on GitHub (pinned to b9d4d170da)
Solutions
- Restore the registry state as of the block's height so every referenced registration is present.
- Align temporal_registry_retention (crates/compression/src/config.rs) between producing and verifying setups.
- If entries legitimately expired, re-register and re-compress the block rather than decompressing against stale registry state.
- Treat occurrences as a verification failure: capture the block and registry roots for forensics instead of retrying.
Defensive patterns
Strategy: try-catch
Try / catch
match compressed_block.decompress(&mut db).await {
Err(e) if e.to_string().contains("Registry root mismatch") => {
// integrity failure: halt verification, preserve the block + registry roots for forensics;
// retrying against the same registry state will fail again
return Err(e.context("registry divergence during fault proving"));
}
r => r?,
} Prevention
- Restore the registry state as of the block's height before verifying old blocks.
- Keep temporal_registry_retention identical on producing and verifying sides (crates/compression/src/config.rs).
- Re-register and re-compress blocks whose registry entries have legitimately expired.
- Treat any occurrence as a verification failure to investigate, never as a transient error.
When it happens
Trigger: Decompressing a V1 compressed block against a registry DB whose contents differ from compression time: registrations pruned after temporal_registry_retention elapsed, registrations at different indices, or an entirely different registry state.
Common situations: Replaying or fault-proving old blocks after registry entries expired; verifier pointed at a different snapshot than the producer; partial registry import; temporal_registry_retention configured differently between compressor and verifier.
Related errors
- Failed to get registry root: {}
- Failed to get registry root: {}
- Missing code for contract: {id}
- Missing utxo for contract: {id}
- Compression level {value} outside of allowed range 0..=22
AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16).
Data as JSON: /api/errors/844a560a397859ad.
Report an issue: GitHub.