Kuberwastaken/claurst · error · anyhow::Error

LSP request ' ' timed out (server: )

Error message

LSP request '{}' timed out (server: {})

What it means

The LSP server did not answer a JSON-RPC request within the library's fixed 30-second timeout; the oneshot receiver registered in `pending` never resolved. The error names both the method and the server so you can tell which language server is unresponsive. The pending entry stays orphaned (its sender is dropped) but the client remains usable for later requests.

Solutions

  1. Check whether the server process is alive (`ps aux | grep <server>`) and inspect its stderr logged at tracing::debug for crashes.
  2. Give the server time to warm up: send initialize and wait for completion before other requests; retry the timed-out request.
  3. Reduce indexing load (smaller workspace, exclude target/node_modules) or pre-warm the server's cache.
  4. Restart the client (`LspClient::start`) if the server is wedged; if timeouts persist for a healthy server, patch the fixed 30s `Duration::from_secs(30)` in send_request_inner to a configurable value.

Example fix

// before
let symbols = client.document_symbols(&uri).await?; // times out on cold server
// after
client.initialize().await?; // let the server finish booting/indexing first
let symbols = tokio::time::timeout(Duration::from_secs(60), client.document_symbols(&uri)).await??;
Defensive patterns

Strategy: retry

Validate before calling

// preflight: ensure the server responds to initialize before issuing time-sensitive requests
let client = LspClient::start(config.clone()).await?;
client.initialize().await.map_err(|e| anyhow::anyhow!("server {name} failed to initialize: {e}"))?;

Try / catch

match tokio::time::timeout(Duration::from_secs(45), client.hover(&uri, pos)).await {
    Err(_) | Ok(Err(e)) if e.to_string().contains("timed out") => {
        warn!("LSP request timed out; retrying once");
        tokio::time::sleep(Duration::from_millis(500)).await;
        client.hover(&uri, pos).await
    }
    Ok(r) => r,
}

Prevention

When it happens

Trigger: Any request (initialize, hover, definition, references, document_symbols, shutdown) sent to a server that is busy indexing, hung, crashed mid-request (response channel closed is a different error, but a crashed-then-silent server can also look like this), or whose stdout pump exited so no dispatch ever happens.

Common situations: `initialize` timing out because the server was still starting on a cold cache (e.g. rust-analyzer first run on a large workspace); hover/definition timing out during heavy indexing; a dead server process whose output pipe produced nothing; extremely slow disk/network-mounted workspaces.

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 Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/d814206d24d108c8. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/core/src/lsp.rs:318

        let body = serde_json::to_string(&msg)?;

        let (tx, rx) = oneshot::channel();
        self.pending.insert(id, tx);

        {
            let writer = self
                .writer
                .as_ref()
                .ok_or_else(|| anyhow::anyhow!("LSP client already shut down"))?;
            let mut w = writer.lock().await;
            send_message(&mut w, &body).await?;
        }

        let response =
            tokio::time::timeout(std::time::Duration::from_secs(30), rx)
                .await
                .map_err(|_| {
                    anyhow::anyhow!(
                        "LSP request '{}' timed out (server: {})",
                        method,
                        self.server_name
                    )
                })?
                .map_err(|_| {
                    anyhow::anyhow!(
                        "LSP request '{}' channel closed (server: {})",
                        method,
                        self.server_name
                    )
                })?;

        if let Some(err) = response.get("error") {
            return Err(anyhow::anyhow!(
                "LSP error from {}: {}",
                self.server_name,
                err

View on GitHub (pinned to b0637c97ec)