{"record":{"id":"74762aa04710f626","repo":"jdx/mise","slug":"expected-rustc-output-escapes-its-output-directory","errorCode":null,"errorMessage":"expected rustc output escapes its output directory","messagePattern":"expected rustc output escapes its output directory","errorType":"exception","errorClass":"eyre::Report","httpStatus":null,"severity":"warning","filePath":"src/cache/rustc.rs","lineNumber":552,"sourceCode":"fn validated_outputs(\n    directory: CacheDirectory,\n    outputs: &RustcOutputs,\n) -> Result<Vec<(CacheFileNode, PathBuf)>> {\n    if directory.version != 1 || !directory.directories.is_empty() || !directory.symlinks.is_empty()\n    {\n        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    }","sourceCodeStart":534,"sourceCodeEnd":570,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/cache/rustc.rs#L534-L570","documentation":"Before pairing cached outputs, validated_outputs() requires every expected output path (outputs.files plus outputs.dep_info) to have its parent exactly equal to outputs.directory (src/cache/rustc.rs:551-553). This bounds the restore destination: an output resolving outside the declared output directory would make the shim write restored files elsewhere, so the restore is refused. Unlike most errors here, this validates the current invocation, not cached data.","triggerScenarios":"RustcInvocation::outputs() computes a path whose parent differs from the output directory: --out-dir joined with a component containing '..', an absolute -o pointing elsewhere, or symlinked working/target directories making the parent comparison fail.","commonSituations":"Custom RUSTFLAGS emitting files with absolute or parent-escaping paths; symlinked CARGO_TARGET_DIR or target dirs; build scripts invoking rustc directly with -o outside --out-dir; unusual out-dir spellings (trailing separators, normalized differently).","solutions":["Inspect the rustc invocation cargo produces (cargo build -v) and check every -o/--out-dir/--emit path stays inside the target dir","Remove or normalize custom RUSTFLAGS that place outputs outside the output directory; avoid symlinked target dirs for cached tasks","If the invocation legitimately writes elsewhere, disable the rust action cache for that task (task rust_cache.enabled = false)"],"exampleFix":"# before: RUSTFLAGS push an output outside the output directory\nexport RUSTFLAGS='--emit=dep-info=../deps/x.d -o ../x.o'\n# after: keep emitted files inside the output directory\nexport RUSTFLAGS='--emit=dep-info=x.d -o x.o'","handlingStrategy":"validation","validationCode":"fn outputs_confined(outputs: &RustcOutputs) -> bool {\n    outputs\n        .files\n        .iter()\n        .chain(std::iter::once(&outputs.dep_info))\n        .all(|p| p.parent() == Some(outputs.directory.as_path()))\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep every --emit/-o path inside the cargo target directory","Avoid symlinked CARGO_TARGET_DIR or working directories in cached tasks","Smoke-test new RUSTFLAGS with one cached build and check for 'escapes its output directory' warnings before rolling them out"],"tags":["rust","rustc","path-validation","outputs"],"backgroundTag":"output-path-escapes-directory","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}