rust-lang/rust-analyzer · error

{join_e}

Error message

{join_e}

What it means

Same `run_session` error handling, but for the case where the main loop finished normally while `io_threads.join()` still returned an error. The IO threads failed (read/write over the transport errored) even though the loop itself saw no fatal event.

Source

Thrown at crates/rust-analyzer/src/session.rs:166

        let not = lsp_server::Notification::new(
            ShowMessageNotification::METHOD.into(),
            ShowMessageParams { kind: MessageType::Warning, message: notice },
        );
        connection.sender.send(lsp_server::Message::Notification(not)).unwrap();
    }

    if config.discover_workspace_config().is_none()
        && !config.has_linked_projects()
        && config.detached_files().is_empty()
    {
        config.rediscover_workspaces();
    }

    // If the io_threads have an error, there's usually an error on the main
    // loop too because the channels are closed. Ensure we report both errors.
    match (crate::main_loop(config, connection), io_threads.join()) {
        (Err(loop_e), Err(join_e)) => anyhow::bail!("{loop_e}\n{join_e}"),
        (Ok(_), Err(join_e)) => anyhow::bail!("{join_e}"),
        (Err(loop_e), Ok(_)) => anyhow::bail!("{loop_e}"),
        (Ok(_), Ok(_)) => {}
    }

    tracing::info!("server did shut down");
    Ok(())
}

fn patch_path_prefix(path: PathBuf) -> PathBuf {
    use std::path::{Component, Prefix};
    if cfg!(windows) {
        // VSCode might report paths with the file drive in lowercase, but this can mess
        // with env vars set by tools and build scripts executed by r-a such that it invalidates
        // cargo's compilations unnecessarily. https://github.com/rust-lang/rust-analyzer/issues/14683
        // So we just uppercase the drive letter here unconditionally.
        // (doing it conditionally is a pain because std::path::Prefix always reports uppercase letters on windows)
        let mut comps = path.components();
        match comps.next() {

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Inspect the joined error text for the underlying OS error (broken pipe, connection reset)
  2. Ensure the client reads all responses and does not close stdout early
  3. Perform a full shutdown/exit handshake before closing the transport
  4. Retry in the client if it was a transient connection issue over a socket-based transport
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(e) = run_session(config, connection).await {
    // io_threads error with clean main loop: inspect transport cause
    eprintln!("io failure: {e:#}");
}

Prevention

When it happens

Trigger: Calling `run_session` where `main_loop` returns `Ok` but `io_threads.join()` returns `Err`, typically when a write to the client fails (client stopped reading / closed stdout) during or at the end of a session.

Common situations: Client closes its stdout-read side early; EPIPE/ECONNRESET writing responses; container/SSH session tearing down the pipe.

Related errors


AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03). Data as JSON: /api/errors/231f960e3bbb1e42. Report an issue: GitHub.