From a448a267e2964928cb10cb2e3f92420fcc334db5 Mon Sep 17 00:00:00 2001 From: jthurlburt Date: Sat, 28 Mar 2026 14:05:43 -0500 Subject: [PATCH 1/2] fix(lsp): read self.context_state instead of stale local variable in _context_get_or_load _context_get_or_load captures self.context_state into a local variable `state` at the top of the method. After _create_lsp_context() or _ensure_context_for_document() successfully updates self.context_state to ContextLoaded, the final check on line 1077 reads the stale local `state` (still NoContext) instead of self.context_state. This causes the method to always raise RuntimeError("Context failed to load") on the first didOpen, even when context loading succeeds. The LSP logs "Loaded SQLMesh Context" then immediately crashes. In VS Code, this puts the client into a permanent "Client got disposed and can't be restarted" state. Signed-off-by: jthurlburt --- sqlmesh/lsp/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sqlmesh/lsp/main.py b/sqlmesh/lsp/main.py index 71dc5e1e2b..b5623f3ff8 100755 --- a/sqlmesh/lsp/main.py +++ b/sqlmesh/lsp/main.py @@ -1074,8 +1074,8 @@ def _context_get_or_load(self, document_uri: t.Optional[URI] = None) -> LSPConte loaded_sqlmesh_message(self.server) else: self._ensure_context_for_document(document_uri) - if isinstance(state, ContextLoaded): - return state.lsp_context + if isinstance(self.context_state, ContextLoaded): + return self.context_state.lsp_context raise RuntimeError("Context failed to load") def _ensure_context_for_document( From 765e58e8df7c9a72419796a435a9e7fb9f4dd225 Mon Sep 17 00:00:00 2001 From: jthurlburt Date: Sun, 7 Jun 2026 13:02:02 -0500 Subject: [PATCH 2/2] Chore(lsp): add regression tests for _context_get_or_load from NoContext Signed-off-by: jthurlburt --- tests/lsp/test_context.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/lsp/test_context.py b/tests/lsp/test_context.py index b463a17139..976d30fef2 100644 --- a/tests/lsp/test_context.py +++ b/tests/lsp/test_context.py @@ -2,6 +2,7 @@ from sqlmesh.core.context import Context from sqlmesh.lsp.context import LSPContext, ModelTarget +from sqlmesh.lsp.main import ContextLoaded, NoContext, SQLMeshLanguageServer from sqlmesh.lsp.uri import URI @@ -61,3 +62,32 @@ def test_lsp_context_run_test(): # Check that the result is not None and has the expected properties assert result is not None assert result.success is True + + +def test_context_get_or_load_from_no_context_with_specified_paths(): + server = SQLMeshLanguageServer(context_class=Context) + server.server.show_message = lambda *args, **kwargs: None + server.specified_paths = [Path("examples/sushi")] + + assert isinstance(server.context_state, NoContext) + + lsp_context = server._context_get_or_load() + + assert isinstance(lsp_context, LSPContext) + assert isinstance(server.context_state, ContextLoaded) + assert server.context_state.lsp_context is lsp_context + + +def test_context_get_or_load_from_no_context_via_workspace_folder(): + server = SQLMeshLanguageServer(context_class=Context) + server.server.show_message = lambda *args, **kwargs: None + server.specified_paths = None + server.workspace_folders = [Path.cwd() / "examples" / "sushi"] + + assert isinstance(server.context_state, NoContext) + + lsp_context = server._context_get_or_load() + + assert isinstance(lsp_context, LSPContext) + assert isinstance(server.context_state, ContextLoaded) + assert server.context_state.lsp_context is lsp_context