jdx/mise · error

recovery content failed verification

Error message

recovery content failed verification

What it means

After loading blob bytes (inline or from disk), read_blob verifies both the length and the SHA-256 digest against the recorded Blob metadata. This bail means the content does not match its recorded hash — the stored preimage is corrupted, tampered with, or was written with a mismatched record. Recovery refuses to restore unverified data.

Source

Thrown at src/system/history/recovery.rs:143

    Ok(())
}

pub(super) fn read_blob(state_dir: &Path, blob: &Blob) -> Result<Vec<u8>> {
    use base64::Engine;
    validate_blob_id(&blob.sha256)?;
    let bytes = match &blob.inline {
        Some(inline) => base64::engine::general_purpose::STANDARD.decode(inline)?,
        None => {
            let path = super::journal::blobs_dir_in(state_dir).join(&blob.sha256);
            let metadata = std::fs::symlink_metadata(&path)?;
            if !metadata.is_file() || metadata.len() != blob.size {
                bail!("invalid recovery content file");
            }
            std::fs::read(path)?
        }
    };
    if bytes.len() as u64 != blob.size || hex::encode(sha2::Sha256::digest(&bytes)) != blob.sha256 {
        bail!("recovery content failed verification");
    }
    Ok(bytes)
}

fn validate_snapshot(state_dir: &Path, snapshot: &PathSnapshot) -> Result<()> {
    match snapshot {
        PathSnapshot::File { content, .. } => {
            read_blob(state_dir, content)?;
        }
        PathSnapshot::Dir {
            files, links, dirs, ..
        } => {
            let mut seen = BTreeSet::new();
            for relative in files
                .iter()
                .map(|f| &f.rel)
                .chain(links.iter().map(|f| &f.rel))
                .chain(dirs.iter().map(|f| &f.rel))

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Do not trust the corrupted preimage; accept the file's current contents with `recover <operation> --keep-current`.
  2. Restore the correct blob from a backup of the state directory so its SHA-256 matches the journal, then rerun recovery.
  3. Reconstruct the intended preimage manually from your dotfiles repo; check storage health (fsck/S.M.A.R.T.) if corruption recurs.

Example fix

// before (corrupt blob)
$ sha256sum blobs/ca9781...
deadbeef...  (expected ca978112...)

// after (restored matching blob)
$ sha256sum blobs/ca9781...
ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb
$ mise bootstrap dotfiles recover
Defensive patterns

Strategy: fallback

Validate before calling

// verify a blob's integrity yourself before recovery
let bytes = std::fs::read(blobs_dir.join(&blob.sha256))?;
use sha2::{Digest, Sha256};
assert_eq!(hex::encode(Sha256::digest(&bytes)), blob.sha256, "blob corrupt");

Try / catch

match result { Err(e) if e.to_string().contains("recovery content failed verification") => /* use --keep-current; restore the blob from a verified backup */, other => other? }

Prevention

When it happens

Trigger: read_blob, called from validate_snapshot (recover_path) or restore, decodes/reads bytes whose hex(SHA-256) != blob.sha256 or whose length != blob.size — e.g. bit rot, an inline base64 payload that decodes to the wrong content, or a blob file rewritten with different data.

Common situations: Disk corruption or a failing SSD, a file-sync client altered state-dir blobs, someone edited a config file's stored preimage, or an interrupted write left the blob inconsistent with the journal.

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


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/83bcd19cb4286f08. Report an issue: GitHub.