fix(vscode): stop the LSP restarting on unrelated configuration changes - #6057
Open
tripleaceme wants to merge 2 commits into
Open
fix(vscode): stop the LSP restarting on unrelated configuration changes#6057tripleaceme wants to merge 2 commits into
tripleaceme wants to merge 2 commits into
Conversation
The extension restarted the language server on every configuration change in the editor and allowed those restarts to overlap, which showed up as `Client got disposed and can't be restarted` plus a stream of `command '...' already exists` errors. Two changes: 1. Filter the configuration event. `extension.ts` subscribed to `workspace.onDidChangeConfiguration`, which fires for every setting in the editor including ones written by other extensions, and restarted unconditionally. It now restarts only when a section the server reads is affected — `sqlmesh` (`projectPaths`, `lspEntrypoint`) or `python.defaultInterpreterPath`. This is why running any python command in a VS Code terminal killed the extension: the Python extension touches its own settings in response, and that was enough to restart the server. It also explains why running a copy of the same interpreter from a different path did not reproduce it, and why a subshell, `su`, `uvx` or a notebook did not either — none of those make the Python extension write a setting. 2. Serialize restarts. `restart()` stops a client and starts another, so concurrent restarts leave the outgoing client's command registrations colliding with the incoming one's and leave requests aimed at a client that has already been disposed. Restarts now run one at a time, with triggers that arrive mid-restart collapsing into a single rerun rather than queueing one restart each. An explicit restart is never downgraded to an automatic one when the two are coalesced. Both helpers are kept free of `vscode` imports so they are covered by `vitest` in the `test-vscode` stage. Fixes SQLMesh#5920 Fixes SQLMesh#5642 Signed-off-by: Adegbite Ayoade <tripleaceme@gmail.com>
This was referenced Sep 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes #5920. Fixes #5642.
Both issues are the same bug: the extension restarted the language server far more often than it needed to, and let those restarts overlap. That produced the errors reported in both —
Client got disposed and can't be restarted, and a stream ofcommand 'sqlmesh.external_model_update_columns' already exists.1. The LSP restarted on every configuration change in the editor
extension.tssubscribed toworkspace.onDidChangeConfigurationand restarted unconditionally:That event fires for any setting in the editor, including settings written by other extensions. It now restarts only when a section the server actually reads is affected —
sqlmesh(which coversprojectPathsandlspEntrypoint, both of which decide how the server is launched) orpython.defaultInterpreterPath(the server runs inside that interpreter).This accounts for the observations in #5642 that were otherwise hard to explain:
su,uvx, a subshell or a notebook did not reproduce it either — none of those go through VS Code's shell integration, so no setting is written.2. Restarts could overlap
LSPClient.restart()isstop()thenstart(). With a burst of triggers, several of those interleave: the outgoing client's command registrations collide with the incoming client's, and in-flight requests are aimed at a client that has already been disposed — exactly the two error messages in the reports.Restarts are now serialized. Triggers arriving mid-restart collapse into a single rerun rather than queueing one restart each, since all a trigger needs is for a restart to have happened after it. An explicit restart (
sqlmesh.restart, sign-in, format) is never downgraded to an automatic one when coalesced with one.A related finding, deliberately not changed here
initializePython()inutilities/common/python.tshas no call sites, soonDidChangePythonInterpreterEventis never fired and theonDidChangePythonInterpreter(() => restartLsp())subscription inextension.tsis currently inert. Two consequences worth flagging:onDidChangeActiveEnvironmentPathwithout comparing against the path it last saw, which would reintroduce a restart storm.I left it alone rather than widen this PR — happy to follow up if you'd like it wired up with a path comparison, or removed.
Test Plan
New unit tests, both in modules kept free of
vscodeimports sovitestcan cover them in thetest-vscodestage:configurationChange.test.ts— restarts forsqlmesh.projectPaths,sqlmesh.lspEntrypointandpython.defaultInterpreterPath; does not restart foreditor.fontSize,workbench.colorTheme,files.autoSave,python.terminal.activateEnvironment,python.analysis.typeCheckingModeorterminal.integrated.env.linux. The last three are the regression tests for the terminal scenario in SQLMesh extension clashes with running sqlmesh in terminal #5642.coalesceAsync.test.ts— runs immediately when idle; four calls during a run collapse into exactly one rerun; every coalesced caller resolves; a later call still runs after a failure.Ran both CI job commands locally:
I have not reproduced the crash end to end in a live VS Code session —
test-vscode-e2eis still disabled, and the trigger needs a real Python extension writing settings. The causal chain is established from the code path plus the reporter's process-of-elimination in #5642, and the unit tests pin the specific settings that must and must not cause a restart. Worth a sanity check from someone who can reproduce the original crash.Checklist
make styleand fixed any issuespnpm run ci; no Python changed, somake fast-testis unaffected by this PRgit commit -s) per the DCOcc @cmgoffena13 — picking this up per your note on #6054. Checked both issues for assignees and linked PRs beforehand; both were unassigned with nothing linked.