Hmbown/CodeWhale · error

LSP initialized notification timed out

Error message

LSP initialized notification timed out

What it means

spawn_with_timeout sent the LSP 'initialized' notification after the initialize response but the handshake future did not complete within the initialize_wait budget; the server accepted initialize but never finished startup in time, so initialization aborts.

Solutions

  1. Check the server process is alive and reading stdin (e.g. ps, strace)
  2. Increase the initialize_wait timeout
  3. Look at server stderr for crash output right after initialize
  4. Ensure no other code holds the transport writer lock

Example fix

// before
.map_err(|_| anyhow!("LSP initialized notification timed out"))??;
// after
.map_err(|_| anyhow!("LSP initialized notification timed out; server may have exited after initialize"))
    .with_context(|| format!("server cmd {:?}", server_cmd)))?;
Defensive patterns

Strategy: retry

Try / catch

timeout(initialize_wait, send_message(&tx, &json!({"jsonrpc":"2.0","method":"initialized","params":{}})))
    .await
    .map_err(|_| anyhow!("initialized notification timed out; is the server still running?"))??;

Prevention

When it happens

Trigger: The `initialized` notification write times out — the server's stdin pipe is full/blocked, the process died right after initialize, or the writer lock is contended.

Common situations: Server crashed immediately after initialize; extremely slow server startup under load; pipe backpressure because the server is not reading stdin.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/eb8cf4fae78cfcf3. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/lsp/client.rs:308

            ));
        }
        if result
            .pointer("/capabilities/positionEncoding")
            .is_some_and(|encoding| encoding.as_str() != Some("utf-16"))
        {
            return Err(anyhow!("LSP server must use UTF-16 positions"));
        }
        timeout(
            initialize_wait,
            send_message(
                &transport.tx_outbound,
                &json!({
                    "jsonrpc": "2.0", "method": "initialized", "params": {}
                }),
            ),
        )
        .await
        .map_err(|_| anyhow!("LSP initialized notification timed out"))??;
        Ok(transport)
    }
}

impl Drop for StdioLspTransport {
    fn drop(&mut self) {
        for task in &self.tasks {
            task.abort();
        }
        if let Ok(mut child) = self.child.try_lock()
            && let Some(child) = child.as_mut()
        {
            let _ = child.start_kill();
        }
        // If shutdown/dispatcher currently owns the child, abort releases
        // its local Child and kill_on_drop remains the final fallback.
    }
}

View on GitHub (pinned to 73e0f67d83)