{"record":{"id":"f36e98018c5ee696","repo":"rust-lang/rust-analyzer","slug":"loop-e-join-e","errorCode":null,"errorMessage":"{loop_e}\n{join_e}","messagePattern":"\\{loop_e\\}\n\\{join_e\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/rust-analyzer/src/session.rs","lineNumber":165,"sourceCode":"        };\n        let not = lsp_server::Notification::new(\n            ShowMessageNotification::METHOD.into(),\n            ShowMessageParams { kind: MessageType::Warning, message: notice },\n        );\n        connection.sender.send(lsp_server::Message::Notification(not)).unwrap();\n    }\n\n    if config.discover_workspace_config().is_none()\n        && !config.has_linked_projects()\n        && config.detached_files().is_empty()\n    {\n        config.rediscover_workspaces();\n    }\n\n    // If the io_threads have an error, there's usually an error on the main\n    // loop too because the channels are closed. Ensure we report both errors.\n    match (crate::main_loop(config, connection), io_threads.join()) {\n        (Err(loop_e), Err(join_e)) => anyhow::bail!(\"{loop_e}\\n{join_e}\"),\n        (Ok(_), Err(join_e)) => anyhow::bail!(\"{join_e}\"),\n        (Err(loop_e), Ok(_)) => anyhow::bail!(\"{loop_e}\"),\n        (Ok(_), Ok(_)) => {}\n    }\n\n    tracing::info!(\"server did shut down\");\n    Ok(())\n}\n\nfn patch_path_prefix(path: PathBuf) -> PathBuf {\n    use std::path::{Component, Prefix};\n    if cfg!(windows) {\n        // VSCode might report paths with the file drive in lowercase, but this can mess\n        // with env vars set by tools and build scripts executed by r-a such that it invalidates\n        // cargo's compilations unnecessarily. https://github.com/rust-lang/rust-analyzer/issues/14683\n        // So we just uppercase the drive letter here unconditionally.\n        // (doing it conditionally is a pain because std::path::Prefix always reports uppercase letters on windows)\n        let mut comps = path.components();","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/rust-lang/rust-analyzer/blob/e8f7e90aa3e7b26aa9a000200f606c1078da99ec/crates/rust-analyzer/src/session.rs#L147-L183","documentation":"In `run_session`, when both the main loop (`crate::main_loop`) and the IO threads (`io_threads.join()`) fail, both errors are combined into one report via `anyhow::bail!(\"{loop_e}\\n{join_e}\")`. This exists so that an IO failure (e.g. broken pipe) that also kills the main loop does not hide either cause.","triggerScenarios":"Calling `rust_analyzer::run_session` where `main_loop` returns `Err` (e.g. error 80) and simultaneously `io_threads.join()` returns `Err` because the client closed/errored the transport.","commonSituations":"Client terminates abruptly over stdio: read/write threads fail and the main loop's channel closes; transport (socket/pipe) dies mid-session.","solutions":["Read both stacked messages: the second line (IO error) usually reveals the root transport failure (e.g. broken pipe)","Fix the client to perform a proper shutdown/exit sequence (see the main-loop error)","Check that the client keeps stdin/stdout open for the lifetime of the session","If run under a supervisor, ensure the process is not being killed or disconnected externally"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match run_session(config, connection).await {\n    Err(e) => eprintln!(\"session failed:\\n{e:#}\"), // both loop and io errors are in the chain\n    Ok(()) => {}\n}","preventionTips":["Keep stdin/stdout pipes open for the whole session lifetime","Perform the shutdown/exit handshake before dropping the connection","Log the full anyhow chain to see both root causes"],"tags":["lsp","rust-analyzer","io","shutdown"],"backgroundTag":"transport-broken-pipe","analyzedSha":"e8f7e90aa3e7b26aa9a000200f606c1078da99ec","analyzedAt":"2026-09-03T21:08:06.959Z","contentChangedAt":"2026-09-03T21:08:06.959Z","schemaVersion":2},"datasetVersion":"2026-09-11T07:07:21.782Z"}