{"record":{"id":"d5b41a054b56e697","repo":"jdx/mise","slug":"cached-rustc-output-set-is-incomplete","errorCode":null,"errorMessage":"cached rustc output set is incomplete","messagePattern":"cached rustc output set is incomplete","errorType":"exception","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/cache/rustc.rs","lineNumber":569,"sourceCode":"            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\"]\n        .into_iter()\n        .map(|name| (name.into(), std::env::var(name).ok()))\n        .collect::<BTreeMap<_, _>>();\n    let responses = session::request_agent(&[AgentRequest::FindExecutableIdentity {\n        executable: executable.clone(),\n        environment: environment.clone(),\n    }])?;\n    let Some(AgentResponse::ExecutableIdentity { stdout }) = responses.into_iter().next() else {\n        bail!(\"cache agent did not return the rustc identity\");\n    };\n    let stdout = if let Some(stdout) = stdout {","sourceCodeStart":551,"sourceCodeEnd":587,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/cache/rustc.rs#L551-L587","documentation":"After pairing each cached file node with an expected output by name, validated_outputs() requires that nothing expected remains unpaired (src/cache/rustc.rs:568-570). The counts already matched and every cached name was found, so leftover expected entries mean the cached set is incomplete for this invocation — reachable only through exotic name duplication or a defensive mismatch, because duplicate cached names trip the 'cached rustc output is unexpected' branch first. It is a defense-in-depth completeness check that refuses partial restores.","triggerScenarios":"directory.files pairing consumed all cached names while expected entries remain: duplicate file nodes in a tampered entry, or a writer/reader version difference in how output names are derived so the pairing silently misaligns.","commonSituations":"Same contexts as the count mismatch: mixed-version shared caches and tampered entries.","solutions":["Treat as the sibling mismatch errors: purge the cache/namespace and rebuild with one mise version","Keep remote namespaces private and version-pinned","Report recurring cases with the action digest — a reachable code path here indicates a writer bug worth filing"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"Same as the count mismatch: any incompletely paired output set invalidates the whole entry — fall back to a full recompile instead of restoring a subset.","preventionTips":["Purge shared caches when upgrading mise versions","Do not hand-edit cache entries","Report reachable occurrences upstream with the action digest — the guard firing indicates a writer-side defect"],"tags":["rust","cache","integrity","outputs","validation"],"backgroundTag":"cache-key-mismatch","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}