{"record":{"id":"591e5f7573c9c6a4","repo":"jdx/mise","slug":"staged-blob-size-does-not-match-the-declared-cas-d","errorCode":null,"errorMessage":"staged blob size does not match the declared CAS digest","messagePattern":"staged blob size does not match the declared CAS digest","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/mise-cache-core/src/local.rs","lineNumber":107,"sourceCode":"        if let Some(existing) = self.find(digest)? {\n            return Ok(existing);\n        }\n        let parent = destination.parent().expect(\"CAS path has a parent\");\n        fs::create_dir_all(parent)?;\n        let staging = tempfile::tempdir_in(parent)?;\n        let temporary = staging.path().join(\"blob\");\n        reflink_copy::reflink_or_copy(source, &temporary)?;\n        let temporary = tempfile::TempPath::try_from_path(temporary)?;\n        make_owner_writable(&temporary)?;\n        fs::OpenOptions::new()\n            .write(true)\n            .open(&temporary)?\n            .sync_all()?;\n        if verify && !digest.matches_file(&temporary)? {\n            bail!(\"staged blob does not match the declared CAS digest\");\n        }\n        if fs::metadata(&temporary)?.len() != digest.size {\n            bail!(\"staged blob size does not match the declared CAS digest\");\n        }\n        match temporary.persist_noclobber(&destination) {\n            Ok(()) => Ok(destination),\n            Err(error) if error.error.kind() == std::io::ErrorKind::AlreadyExists => self\n                .find(digest)?\n                .ok_or_else(|| eyre::eyre!(\"concurrent CAS write did not publish a valid blob\")),\n            Err(error) => Err(error.error.into()),\n        }\n    }\n\n    fn store_with(\n        &self,\n        digest: &CacheDigest,\n        write: impl FnOnce(&mut tempfile::NamedTempFile) -> Result<()>,\n    ) -> Result<PathBuf> {\n        let destination = self.path_for(digest)?;\n        if let Some(existing) = self.find(digest)? {\n            return Ok(existing);","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/crates/mise-cache-core/src/local.rs#L89-L125","documentation":"After staging a file into the CAS, the staged file's byte length must equal `digest.size`. This check primarily guards the pre-verified fast path (`store_verified_file`, used when this crate already verified the content earlier): if the source's length changed between that verification and the staging copy, publishing is refused rather than storing a truncated or extended blob under a wrong size. When full verification runs (`store_file`), the digest check already includes size and would fail first.","triggerScenarios":"The source file is appended to or truncated between the crate's earlier verification pass and the staging copy on the verified fast path - e.g. a log or output file still being written when it reaches the CAS.","commonSituations":"Mutable build outputs passed to the verified store path; writers racing the cache layer; files on synchronizing network mounts changing length between passes.","solutions":["Only pass fully-written, quiesced files to the CAS; finalize outputs before caching them.","Recompute the digest from the current file with `CacheDigest::blake3_file` and use the verifying `store_file` instead of a pre-verified path.","Retry after the writer has finished."],"exampleFix":"// before: digest verified earlier, file grew since - size check bails\ncas.store_verified_file(&digest, &path);\n\n// after: hash the current file and use the verifying store\nlet digest = CacheDigest::blake3_file(&path)?;\ncas.store_file(&digest, &path)?;","handlingStrategy":"validation","validationCode":"let len = std::fs::metadata(&source)?.len();\nif len != digest.size {\n    // the file changed since the digest was verified; recompute it\n    let digest = CacheDigest::blake3_file(&source)?;\n    return cas.store_file(&digest, &source);\n}\ncas.store_verified_file(&digest, &source)?;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass only quiesced, fully-written files to pre-verified store paths.","Re-derive the digest with blake3_file when any doubt exists about file stability.","Retry after concurrent writers (builds, loggers) have exited."],"tags":["local-cache","cas","size-mismatch","race-condition","rust"],"backgroundTag":"size-mismatch","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}