{"id":"9a5ef3558b23c1ae","repo":"rust-lang/rust","slug":"error-writing-pre-lto-bitcode-file","errorCode":null,"errorMessage":"Error writing pre-lto-bitcode file `{}`: {}","messagePattern":"Error writing pre-lto-bitcode file `(.+?)`: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/rustc_codegen_ssa/src/back/write.rs","lineNumber":859,"sourceCode":"    // If we're doing some form of incremental LTO then we need to be sure to\n    // save our module to disk first.\n    let bitcode = if cgcx.module_config.emit_pre_lto_bc {\n        let filename = pre_lto_bitcode_filename(&module.name);\n        cgcx.incr_comp_session_dir.as_ref().map(|path| path.join(&filename))\n    } else {\n        None\n    };\n\n    match lto_type {\n        ComputedLtoType::No => {\n            let module = B::codegen(cgcx, &prof, &shared_emitter, module, &cgcx.module_config);\n            WorkItemResult::Finished(module)\n        }\n        ComputedLtoType::Thin => {\n            let thin_buffer = B::serialize_module(module.module_llvm, true);\n            if let Some(path) = bitcode {\n                fs::write(&path, thin_buffer.data()).unwrap_or_else(|e| {\n                    panic!(\"Error writing pre-lto-bitcode file `{}`: {}\", path.display(), e);\n                });\n            }\n            WorkItemResult::NeedsThinLto(module.name, thin_buffer)\n        }\n        ComputedLtoType::Fat => match bitcode {\n            Some(path) => {\n                let buffer = B::serialize_module(module.module_llvm, false);\n                fs::write(&path, buffer.data()).unwrap_or_else(|e| {\n                    panic!(\"Error writing pre-lto-bitcode file `{}`: {}\", path.display(), e);\n                });\n                WorkItemResult::NeedsFatLto(FatLtoInput::Serialized {\n                    name: module.name,\n                    bitcode_path: path,\n                })\n            }\n            None => WorkItemResult::NeedsFatLto(FatLtoInput::InMemory(module)),\n        },\n    }","sourceCodeStart":841,"sourceCodeEnd":877,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_codegen_ssa/src/back/write.rs#L841-L877","documentation":"Raised in the Thin-LTO branch of execute_optimize_work_item when rustc has just serialized a module to a thin bitcode buffer and fails to persist it to the incremental-compilation session directory. The panic happens in fs::write to a path computed from incr_comp_session_dir joined with pre_lto_bitcode_filename(module.name). It indicates an I/O failure writing the pre-LTO .bc artifact that downstream Thin-LTO steps will need to read back.","triggerScenarios":"cgcx.module_config.emit_pre_lto_bc is true (incremental + Thin LTO), lto_type is ComputedLtoType::Thin, and fs::write(&path, thin_buffer.data()) returns Err. Concrete causes: target/incremental dir deleted or moved mid-build, disk full, out of inodes, path on a read-only mount, antivirus locking the file on Windows, or a permission drop between rustc spawns.","commonSituations":"CI disk pressure; cleaning target/ while a build runs; building into an NFS/SMB share; Windows Defender scanning .bc files; cargo workspace where one member wipes the shared target dir; out-of-space on small cloud VMs.","solutions":["Free disk space and inodes on the volume holding target/","Stop concurrent processes touching target/incremental (e.g. another cargo invocation or a cleaner)","Move target to a local non-network directory: CARGO_TARGET_DIR=/local/target","Disable incremental compilation if the env is hostile: CARGO_INCREMENTAL=0","On Windows, exclude the target dir from antivirus scanning"],"exampleFix":"# before (disk full / shared FS)\nCARGO_INCREMENTAL=1 cargo build\n# after\nCARGO_INCREMENTAL=0 cargo build","handlingStrategy":"try-catch","validationCode":"use std::path::{Path, PathBuf};\nuse std::fs;\n\nfn preflight_write_pre_lto(path: &Path) -> std::io::Result<()> {\n    if let Some(parent) = path.parent() {\n        if !parent.as_os_str().is_empty() && !parent.exists() {\n            fs::create_dir_all(parent)?;\n        }\n        // probe writability without committing the real file\n        let probe = parent.join(format!(\".{}.writeprobe\", path.file_name().and_then(|s| s.to_str()).unwrap_or(\"tmp\")));\n        fs::write(&probe, b\"\")?;\n        fs::remove_file(&probe)?;\n    }\n    if path.exists() && fs::metadata(path)?.permissions().readonly() {\n        return Err(std::io::Error::new(\n            std::io::ErrorKind::PermissionDenied,\n            \"pre-lto-bitcode target is read-only\",\n        ));\n    }\n    let free = fs2::free_space(path.parent().unwrap_or(Path::new(\".\")))\n        .ok();\n    if let Some(b) = free {\n        if b < 256 * 1024 * 1024 {\n            return Err(std::io::Error::new(\n                std::io::ErrorKind::Other,\n                \"insufficient free space for pre-lto-bitcode write\",\n            ));\n        }\n    }\n    Ok(())\n}\n\n// before invoking codegen: preflight_write_pre_lto(&out_path)?;","typeGuard":"// Write errors here are thin wrappers around std::io::Error; narrow on kind.\nfn is_pre_lto_write_error(e: &std::io::Error) -> bool {\n    matches!(\n        e.kind(),\n        std::io::ErrorKind::PermissionDenied\n            | std::io::ErrorKind::NotFound\n            | std::io::ErrorKind::StorageFull\n            | std::io::ErrorKind::ReadOnlyFilesystem\n            | std::io::ErrorKind::QuotaExceeded\n    )\n}","tryCatchPattern":"match write_pre_lto_bitcode(&path, &bitcode) {\n    Ok(()) => {},\n    Err(ref e) if e.kind() == std::io::ErrorKind::StorageFull\n               || e.kind() == std::io::ErrorKind::QuotaExceeded => {\n        // non-retryable resource exhaustion; surface to caller, do NOT silently drop\n        return Err(format!(\"disk/quota full writing {}: {}\", path.display(), e).into());\n    }\n    Err(ref e) if e.kind() == std::io::ErrorKind::PermissionDenied\n               || e.kind() == std::io::ErrorKind::ReadOnlyFilesystem => {\n        return Err(format!(\"permission denied writing {}: {}\", path.display(), e).into());\n    }\n    Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {\n        // single bounded retry; Interrupted is explicitly documented as retryable\n        write_pre_lto_bitcode(&path, &bitcode)\n            .map_err(|e| format!(\"retry failed writing {}: {}\", path.display(), e))?;\n    }\n    Err(e) => return Err(format!(\"error writing pre-lto-bitcode {}: {}\", path.display(), e).into()),\n}","preventionTips":["Write pre-lto-bitcode to a temp file in the same directory as the final path, fsync, then rename atomically; avoids partial-file corruption on crash.","Create the destination directory with create_dir_all before codegen starts, not mid-write.","Reserve disk headroom (e.g. >=2x expected bitcode size) before running parallel codegen units.","Do not run the writer with read-only mounts or snapshot directories as output targets.","Handle EINTR explicitly: std I/O can surface Interrupted and a single retry is correct."],"tags":["lto","incremental","filesystem","codegen","io"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}