{"record":{"id":"d903d83bc1e93409","repo":"jdx/mise","slug":"invalid-recovery-content-file","errorCode":null,"errorMessage":"invalid recovery content file","messagePattern":"invalid recovery content file","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/recovery.rs","lineNumber":137,"sourceCode":"}\n\nfn validate_blob_id(hash: &str) -> Result<()> {\n    if hash.len() != 64 || !hash.bytes().all(|b| b.is_ascii_hexdigit()) {\n        bail!(\"invalid recovery content identifier\");\n    }\n    Ok(())\n}\n\npub(super) fn read_blob(state_dir: &Path, blob: &Blob) -> Result<Vec<u8>> {\n    use base64::Engine;\n    validate_blob_id(&blob.sha256)?;\n    let bytes = match &blob.inline {\n        Some(inline) => base64::engine::general_purpose::STANDARD.decode(inline)?,\n        None => {\n            let path = super::journal::blobs_dir_in(state_dir).join(&blob.sha256);\n            let metadata = std::fs::symlink_metadata(&path)?;\n            if !metadata.is_file() || metadata.len() != blob.size {\n                bail!(\"invalid recovery content file\");\n            }\n            std::fs::read(path)?\n        }\n    };\n    if bytes.len() as u64 != blob.size || hex::encode(sha2::Sha256::digest(&bytes)) != blob.sha256 {\n        bail!(\"recovery content failed verification\");\n    }\n    Ok(bytes)\n}\n\nfn validate_snapshot(state_dir: &Path, snapshot: &PathSnapshot) -> Result<()> {\n    match snapshot {\n        PathSnapshot::File { content, .. } => {\n            read_blob(state_dir, content)?;\n        }\n        PathSnapshot::Dir {\n            files, links, dirs, ..\n        } => {","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/recovery.rs#L119-L155","documentation":"read_blob loads preimage content either from an inline base64 blob or from a file under the state directory's blobs dir. This bail fires for the file case when the on-disk blob is missing as a regular file (missing, symlink, directory) or its length does not match blob.size. Recovery refuses to restore content it cannot trust to be complete.","triggerScenarios":"read_blob -> symlink_metadata on `blobs_dir_in(state_dir)/<sha256>` shows the entry is not a plain file, or metadata.len() != blob.size; happens when validating a snapshot in recover_path or when a restore re-reads content.","commonSituations":"A blob file was deleted (cleanup ran too early, or the user pruned the state dir), a partial write left a truncated blob, or a sync/tool replaced the blob with something else.","solutions":["Check the blobs directory in the mise state dir; if the blob is simply gone, the preimage cannot be restored — accept current contents with `recover <operation> --keep-current`.","If a truncated/partial blob exists from a partial write, delete the whole pending record rather than trusting it.","Restore the blob file from a backup of the state directory, ensuring its byte length matches blob.size, then rerun recovery."],"exampleFix":"// before\n$ ls ~/.local/share/mise/history/blobs/\nca9781...  (truncated: 12 bytes, expected 4096)\n\n// after (restore blob from backup, size matches)\n$ cp backup/blobs/ca9781... ~/.local/share/mise/history/blobs/\n$ mise bootstrap dotfiles recover","handlingStrategy":"fallback","validationCode":"// verify blobs exist and sizes match before recovery\nlet blob_path = blobs_dir.join(&blob.sha256);\nlet meta = std::fs::symlink_metadata(&blob_path)?;\nassert!(meta.is_file() && meta.len() == blob.size, \"blob missing or wrong size\");","typeGuard":null,"tryCatchPattern":"match result { Err(e) if e.to_string().contains(\"invalid recovery content file\") => /* fall back to --keep-current or restore blob from backup */, other => other? }","preventionTips":["Do not prune or clean the mise state directory while operations are pending","Back up the blobs directory alongside journals","Complete or discard interrupted operations promptly so cleanup ordering stays valid"],"tags":["blob","filesystem","file-size","recovery"],"backgroundTag":"file-not-found","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}