Skip to content

Commit 6d27ccb

Browse files
xlemaitre-pocbug-ops
authored andcommitted
fix(lsp): address review feedback on background init (#172)
- clear expected languages after init so a partially-failed language falls back to NoServerForLanguage instead of ServerInitializing forever - log skipped non-object JSON at debug, not warn (OmniSharp bursts at startup) - reword "wait a few seconds" -> "wait and retry / may take minutes" (error + docs)
1 parent 491aa8c commit 6d27ccb

5 files changed

Lines changed: 15 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2828

2929
- **Initialize handshake timeout** — the LSP `initialize` request now honors the per-server `timeout_seconds` configuration instead of a hardcoded 30 s, so servers that need longer to load on large projects are no longer killed mid-initialization. (#172)
3030
- **Startup `null` messages** — the LSP receive loop skips bare `null`/non-object JSON-RPC messages (emitted by OmniSharp during startup) and keeps reading, instead of treating them as a fatal protocol error that drops the connection. (#172)
31+
- **Partial-success expected languages** — the "expected languages" set is cleared once background initialization completes, so a language whose server failed to spawn (when others succeeded) falls back to `NoServerForLanguage` instead of reporting `ServerInitializing` forever. (#172)
3132

3233
- **ServerCancelled retry**`LspClient::request()` now retries up to 3 times with exponential backoff (500 ms → 1 s → 2 s) when an LSP server returns error code -32802 with `data.retriggerRequest: true`, instead of propagating the error immediately to the MCP caller (#128)
3334
- **Integration test readiness gate** — Replaced `publishDiagnostics`-based readiness signal with hover-probe polling (3 consecutive successful hover responses required), matching the ra_e2e approach; fixes 3 of 5 integration tests that failed consistently in isolation after PR #123 (#127)

crates/mcpls-core/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ pub enum Error {
6666

6767
/// LSP server for the language is configured but still initializing.
6868
#[error(
69-
"LSP server for language '{0}' is still initializing (large project load in progress); wait a few seconds and retry the request"
69+
"LSP server for language '{0}' is still initializing (large project load in progress); wait and retry the request (this may take a few minutes on large projects)"
7070
)]
7171
ServerInitializing(String),
7272

crates/mcpls-core/src/lib.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,13 @@ fn spawn_lsp_servers_background(
417417
let server_count = result.server_count();
418418
let notification_receivers = {
419419
let mut t = translator.lock().await;
420-
register_servers(result, &mut t)
420+
let receivers = register_servers(result, &mut t);
421+
// Background initialization has completed; stop reporting "still
422+
// initializing" (especially for languages whose server failed to
423+
// spawn on partial success, which would otherwise return
424+
// ServerInitializing forever instead of NoServerForLanguage).
425+
t.clear_expected_languages();
426+
receivers
421427
};
422428
info!("Proceeding with {} LSP server(s)", server_count);
423429

crates/mcpls-core/src/lsp/transport.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use std::collections::HashMap;
1313
use serde_json::Value;
1414
use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader};
1515
use tokio::process::{ChildStdin, ChildStdout};
16-
use tracing::{trace, warn};
16+
use tracing::{debug, trace, warn};
1717

1818
use crate::error::{Error, Result};
1919
use crate::lsp::types::{InboundMessage, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse};
@@ -111,7 +111,10 @@ impl LspTransport {
111111
// (or other non-object) JSON-RPC message. Skip it and read the next
112112
// framed message instead of killing the whole message loop.
113113
if !value.is_object() {
114-
warn!("Skipping non-object LSP message: {}", value);
114+
// Some servers (notably OmniSharp) emit a burst of these during
115+
// startup; log at debug to avoid flooding the logs for what is a
116+
// recoverable, expected condition.
117+
debug!("Skipping non-object LSP message: {}", value);
115118
continue;
116119
}
117120

docs/user-guide/troubleshooting.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ mcpls --log-level debug
233233
roots = ["/Users/username/current-project"]
234234
```
235235

236-
**Note**: `timeout_seconds` also bounds the initial `initialize` handshake, so raising it (Solution 1) is what helps a server that needs minutes to load a large solution. While a configured server is still initializing, tool calls for that language return a "server is still initializing - wait a few seconds and retry" message rather than a hard "no server configured" error.
236+
**Note**: `timeout_seconds` also bounds the initial `initialize` handshake, so raising it (Solution 1) is what helps a server that needs minutes to load a large solution. While a configured server is still initializing, tool calls for that language return a "server is still initializing - wait and retry" message (loading a large solution may take a few minutes) rather than a hard "no server configured" error.
237237

238238
### "rust-analyzer indexing takes forever"
239239

0 commit comments

Comments
 (0)