{"record":{"id":"7c7d2cda555d2869","repo":"jdx/mise","slug":"cached-rustc-output-failed-digest-verification","errorCode":null,"errorMessage":"cached rustc output failed digest verification: {}","messagePattern":"cached rustc output failed digest verification: (.+?)","errorType":"exception","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/cache/rustc.rs","lineNumber":389,"sourceCode":"}\n\nfn stage_cached_output(\n    directory: &Path,\n    index: usize,\n    source: &Path,\n    node: &CacheFileNode,\n) -> Result<tempfile::TempPath> {\n    let temporary = directory.join(format!(\"output-{index}\"));\n    reflink_copy::reflink_or_copy(source, &temporary)\n        .wrap_err_with(|| format!(\"failed to materialize cached rustc output {}\", node.name))?;\n    let temporary = tempfile::TempPath::try_from_path(temporary)?;\n    make_owner_writable(&temporary)?;\n    std::fs::OpenOptions::new()\n        .write(true)\n        .open(&temporary)?\n        .sync_all()?;\n    if !node.digest.matches_file(&temporary)? {\n        bail!(\n            \"cached rustc output failed digest verification: {}\",\n            node.name\n        );\n    }\n    apply_file_mode(&temporary, node.mode)?;\n    Ok(temporary)\n}\n\nfn cached_matches(cached: &CachedCompilation, output: &Output) -> bool {\n    output.status.success()\n        && cached.stdout == output.stdout\n        && cached.stderr == output.stderr\n        && cached.outputs.iter().all(|expected| {\n            std::fs::metadata(&expected.path).is_ok_and(|metadata| {\n                file_mode(&metadata) == expected.mode\n                    && expected\n                        .digest\n                        .matches_file(&expected.path)","sourceCodeStart":371,"sourceCodeEnd":407,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/cache/rustc.rs#L371-L407","documentation":"Before restoring a cached output file, the wrapper materializes the blob (reflink/copy) into a temp file and re-hashes it; the digest must match the node's recorded digest. A mismatch means the stored bytes degraded — corruption, truncation, or a bad copy — and the wrapper refuses to install wrong build artifacts (src/cache/rustc.rs:389).","triggerScenarios":"Bit rot or truncated writes in the cache blob store; reflink/copy silently producing wrong bytes on an exotic filesystem; tampered or partially synced cache directory (rsync/interrupted disk full).","commonSituations":"Shared/network caches copied without full sync; disk-full during an earlier record; filesystems with buggy reflink support.","solutions":["Delete the corrupted blob/cache store and rebuild so outputs are re-recorded","Check disk space and filesystem health if multiple outputs fail verification","If using reflink on an unusual FS, disable the rustc cache or copy the store fully before use"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"# Digest failure => corrupted blob: delete store, rebuild uncached\nif grep -q 'failed digest verification' build.log; then\n  rm -rf \"$MISE_CACHE_STAGING_DIR\"; cargo build\nfi","preventionTips":["Sync cache stores fully (no partial rsync) before use","Monitor disk space during record phases","Treat repeated verification failures across files as FS/hardware trouble"],"tags":["mise","rustc-cache","cache-corruption","digest-verification","integrity"],"backgroundTag":"cache-corruption","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}