From 58225e16071f0e1d729c4df1e2c1705f8d80ba29 Mon Sep 17 00:00:00 2001 From: Nishchay Mahor Date: Sat, 4 Jul 2026 18:15:15 -0700 Subject: [PATCH] fix: error when invalidating a nonexistent environment sqlmesh invalidate ENVIRONMENT reported success even when the environment did not exist, so a mistyped name looked like it worked. Check the environment exists first and raise a clear error (nonzero exit) otherwise. Fixes #5621 Signed-off-by: Nishchay Mahor --- sqlmesh/core/context.py | 2 ++ tests/core/test_context.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/sqlmesh/core/context.py b/sqlmesh/core/context.py index 5902977331..d9cc1dba20 100644 --- a/sqlmesh/core/context.py +++ b/sqlmesh/core/context.py @@ -1856,6 +1856,8 @@ def invalidate_environment(self, name: str, sync: bool = False) -> None: be deleted asynchronously by the janitor process. """ name = Environment.sanitize_name(name) + if self.state_sync.get_environment(name) is None: + raise SQLMeshError(f"Environment '{name}' does not exist.") self.state_sync.invalidate_environment(name) if sync: self._cleanup_environments(name=name) diff --git a/tests/core/test_context.py b/tests/core/test_context.py index 365d31d3fd..83b75c4d26 100644 --- a/tests/core/test_context.py +++ b/tests/core/test_context.py @@ -1611,6 +1611,20 @@ def test_invalidate_environment_no_sync_skips_cleanup(sushi_context, mocker: Moc state_sync_mock.delete_expired_environments.assert_not_called() +def test_invalidate_environment_nonexistent_raises(sushi_context, mocker: MockerFixture) -> None: + """Invalidating an environment that does not exist should error instead of + reporting success, so a mistyped name is caught rather than silently accepted.""" + state_sync_mock = mocker.patch.object( + type(sushi_context), "state_sync", new_callable=mocker.PropertyMock + ).return_value + state_sync_mock.get_environment.return_value = None + + with pytest.raises(SQLMeshError, match="Environment 'doesnotexist' does not exist"): + sushi_context.invalidate_environment("doesnotexist") + + state_sync_mock.invalidate_environment.assert_not_called() + + @pytest.mark.slow def test_plan_default_end(sushi_context_pre_scheduling: Context): prod_plan_builder = sushi_context_pre_scheduling.plan_builder("prod")