{"record":{"id":"1222c3118615f244","repo":"jdx/mise","slug":"cached-rustc-output-set-does-not-match-the-invocat","errorCode":null,"errorMessage":"cached rustc output set does not match the invocation","messagePattern":"cached rustc output set does not match the invocation","errorType":"exception","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/cache/rustc.rs","lineNumber":558,"sourceCode":"        bail!(\"cached rustc output directory has unsupported entries\");\n    }\n    let mut expected = outputs\n        .files\n        .iter()\n        .chain(std::iter::once(&outputs.dep_info))\n        .map(|path| {\n            let name = path\n                .file_name()\n                .and_then(|name| name.to_str())\n                .ok_or_else(|| eyre::eyre!(\"expected rustc output name is not UTF-8\"))?;\n            if path.parent() != Some(outputs.directory.as_path()) {\n                bail!(\"expected rustc output escapes its output directory\");\n            }\n            Ok((name.to_string(), path.clone()))\n        })\n        .collect::<Result<BTreeMap<_, _>>>()?;\n    if directory.files.len() != expected.len() {\n        bail!(\"cached rustc output set does not match the invocation\");\n    }\n    let mut files = Vec::with_capacity(directory.files.len());\n    for node in directory.files {\n        validate_file_mode(&node)?;\n        let destination = expected\n            .remove(&node.name)\n            .ok_or_else(|| eyre::eyre!(\"cached rustc output is unexpected: {}\", node.name))?;\n        files.push((node, destination));\n    }\n    if !expected.is_empty() {\n        bail!(\"cached rustc output set is incomplete\");\n    }\n    Ok(files)\n}\n\nfn compiler_identity(rustc: &OsStr) -> Result<CompilerIdentity> {\n    let executable = resolve_executable(rustc)?;\n    let environment = [\"RUSTUP_HOME\", \"RUSTUP_TOOLCHAIN\"]","sourceCodeStart":540,"sourceCodeEnd":576,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/cache/rustc.rs#L540-L576","documentation":"validated_outputs() requires the cached directory.files count to equal the number of outputs the current invocation expects (emitted files plus the dep-info file) (src/cache/rustc.rs:557-559). A count mismatch means the cached entry does not describe this invocation's output set — the action digest and the stored directory disagree, which in practice means cross-version cache pollution or a tampered/poisoned entry; the restore is refused rather than partially applied.","triggerScenarios":"A stored CacheDirectory with extra or missing file nodes relative to the invocation's expected outputs: a remote cache shared across mise versions whose output-set parsing differs (e.g. dep-info handling changed), or a deliberately crafted entry aimed at the CAS.","commonSituations":"Mixed mise versions writing one remote namespace; shared CI caches across teams on different versions; a compromised or untrusted remote cache.","solutions":["Purge the affected cache/namespace and rebuild from a single trusted mise version","Keep one mise version per namespace and keep namespaces private per project/team","If it persists on a private cache with matched versions, investigate integrity — this guard exists to block poisoned entries from being restored"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"fn output_count_matches(directory: &CacheDirectory, outputs: &RustcOutputs) -> bool {\n    directory.files.len() == outputs.files.len() + 1 // + dep-info\n}","typeGuard":null,"tryCatchPattern":"Refuse partial restores: on any set mismatch, discard the cached entry entirely and recompile — never merge cached files with locally produced ones.","preventionTips":["One mise version per remote namespace","Keep namespaces private per project/team","Investigate persistent mismatches as potential cache poisoning, not flakiness"],"tags":["rust","cache","integrity","cache-poisoning","outputs"],"backgroundTag":"cache-key-mismatch","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}