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
- Check whether the server process is alive (`ps aux | grep <server>`) and inspect its stderr logged at tracing::debug for crashes.
- Give the server time to warm up: send initialize and wait for completion before other requests; retry the timed-out request.
- Reduce indexing load (smaller workspace, exclude target/node_modules) or pre-warm the server's cache.
- 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
- Always complete initialize before other requests so the server is fully booted.
- Pre-warm server caches or restrict workspace scope to keep indexing time down.
- Wrap requests in your own outer timeout with one retry, and fall back gracefully (return None for hover/definition).
- Monitor server liveness via its stderr (tracing::debug) and restart wedged clients.
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.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- LSP error from
- Failed to start LSP server
- LSP server stdin not available
- LSP server stdout not available
- LSP client already shut down
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)