Skip to content

fix: LSP server should only exit on the exit notification#6001

Merged
max-sixty merged 1 commit into
mainfrom
fix/lsp-notification-exit
Jun 14, 2026
Merged

fix: LSP server should only exit on the exit notification#6001
max-sixty merged 1 commit into
mainfrom
fix/lsp-notification-exit

Conversation

@prql-bot

Copy link
Copy Markdown
Collaborator

Problem

The LSP server's main_loop returned Ok(()) on any notification:

Message::Notification(not) => {
    eprintln!("got notification: {not:?}");
    return Ok(());
}

Connection::initialize() consumes the initialized notification during the handshake, so the first notification reaching main_loop in a real session is something like textDocument/didOpen or $/setTrace — and the server would shut down the moment a client sent one. Worse, returning early drops the connection's sender, so a follow-up message panics with sending on a disconnected channel and the process exits with code 1.

The existing lsp test only sent an exit notification, so it never exercised a non-exit notification and masked the bug.

Solution

Only the exit notification stops the loop; other notifications are logged and ignored.

Message::Notification(not) => {
    eprintln!("got notification: {not:?}");
    if not.method == "exit" {
        return Ok(());
    }
}

Testing

Added lsp_ignores_non_exit_notification, which sends a $/setTrace notification before exit. Before the fix it fails with success: false, exit_code: 1, and sending on a disconnected channel; after the fix the server logs the notification, continues, and shuts down cleanly on exit. The existing lsp test is unchanged.

Found during the nightly code-quality survey.

The main loop returned on any notification, so the first non-exit
notification a client sent (e.g. textDocument/didOpen) shut the server
down. Returning also dropped the connection sender, panicking with
"sending on a disconnected channel".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@prql-bot prql-bot left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix is correct — exit is the right notification to terminate on, and ignoring others matches LSP semantics. I confirmed both lsp tests pass once the binary is built with --features lsp.

One observation, not a blocker: this regression test won't run in CI. No test job enables the lsp feature — the test-rust matrix uses features: default,test-dbs-external and default (tests.yaml), lsp isn't in default (default = ["cli"]), and no workflow uses --all-features. So the #[cfg(feature = "lsp")] tests — both the new one and the pre-existing lsp test — are never compiled or run in CI; they only guard the fix for developers who run cargo test --features lsp locally.

This gap predates the PR (the existing lsp test has it too), so it's reasonable to leave as-is given lsp is a stub feature. But since the PR's value is the added regression coverage, a maintainer may want to add an lsp-enabled leg to the CI matrix in a follow-up so the test actually protects the fix.

@max-sixty max-sixty merged commit 7440807 into main Jun 14, 2026
38 checks passed
@max-sixty max-sixty deleted the fix/lsp-notification-exit branch June 14, 2026 16:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants