From 7cec62ab0aee035df53c169786d2f56f5f9ace7e Mon Sep 17 00:00:00 2001 From: Harshit28j Date: Mon, 16 Mar 2026 17:55:04 +0530 Subject: [PATCH] fix: prevent migrations from being marked as applied when DDL didn't execute Remove overly broad "does not exist" pattern from idempotent error detection that was silently marking failed migrations as applied. Also fail hard in _resolve_all_migrations when diff application fails instead of silently proceeding to mark all migrations as applied. Fixes issue where users upgrading LiteLLM see migrations recorded as "applied" in _prisma_migrations while actual schema changes never happened. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../litellm_proxy_extras/utils.py | 16 +++++++++++++--- .../test_litellm_proxy_extras_utils.py | 19 ++++++++++++------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/litellm-proxy-extras/litellm_proxy_extras/utils.py b/litellm-proxy-extras/litellm_proxy_extras/utils.py index 7eff0c00f75..cdc8c95972a 100644 --- a/litellm-proxy-extras/litellm_proxy_extras/utils.py +++ b/litellm-proxy-extras/litellm_proxy_extras/utils.py @@ -234,7 +234,6 @@ class ProxyExtrasDBManager: r"duplicate key value violates", r"relation .* already exists", r"constraint .* already exists", - r"does not exist", r"Can't drop database.* because it doesn't exist", ] @@ -326,9 +325,20 @@ class ProxyExtrasDBManager: logger.info(f"prisma db execute stdout: {result.stdout}") logger.info("✅ Migration diff applied successfully") except subprocess.CalledProcessError as e: - logger.warning(f"Failed to apply migration diff: {e.stderr}") + logger.error( + f"Failed to apply migration diff: {e.stderr}. " + f"Will NOT mark migrations as applied." + ) + raise RuntimeError( + f"Migration diff application failed. Migrations will not be marked as applied. " + f"Please check the database state and apply the diff manually. Error: {e.stderr}" + ) from e except subprocess.TimeoutExpired: - logger.warning("Migration diff application timed out.") + logger.error("Migration diff application timed out. Will NOT mark migrations as applied.") + raise RuntimeError( + "Migration diff application timed out. Migrations will not be marked as applied. " + "Please check the database state and apply the diff manually." + ) # 3. Mark all migrations as applied if not mark_all_applied: diff --git a/tests/litellm-proxy-extras/test_litellm_proxy_extras_utils.py b/tests/litellm-proxy-extras/test_litellm_proxy_extras_utils.py index c1e3fd5072f..1d610633664 100644 --- a/tests/litellm-proxy-extras/test_litellm_proxy_extras_utils.py +++ b/tests/litellm-proxy-extras/test_litellm_proxy_extras_utils.py @@ -109,9 +109,19 @@ class TestIdempotentErrorDetection: error_message = "constraint 'fk_user_id' already exists" assert ProxyExtrasDBManager._is_idempotent_error(error_message) is True - def test_is_idempotent_error_does_not_exist(self): - """Test detection of 'does not exist' error""" + def test_is_idempotent_error_does_not_exist_is_not_idempotent(self): + """Generic 'does not exist' errors are NOT idempotent — they indicate real failures""" error_message = "ERROR: index 'idx' does not exist" + assert ProxyExtrasDBManager._is_idempotent_error(error_message) is False + + def test_is_idempotent_error_relation_does_not_exist_is_not_idempotent(self): + """Relation 'does not exist' errors are NOT idempotent""" + error_message = 'ERROR: relation "LiteLLM_DailyAgentSpend" does not exist' + assert ProxyExtrasDBManager._is_idempotent_error(error_message) is False + + def test_is_idempotent_error_cant_drop_database_is_idempotent(self): + """Can't drop database because it doesn't exist IS idempotent""" + error_message = "Can't drop database 'litellm' because it doesn't exist" assert ProxyExtrasDBManager._is_idempotent_error(error_message) is True def test_is_idempotent_error_case_insensitive(self): @@ -119,11 +129,6 @@ class TestIdempotentErrorDetection: error_message = "COLUMN 'ID' ALREADY EXISTS" assert ProxyExtrasDBManager._is_idempotent_error(error_message) is True - def test_is_idempotent_error_does_not_exist(self): - """Test detection of 'does not exist' error""" - error_message = "ERROR: index 'idx' does not exist" - assert ProxyExtrasDBManager._is_idempotent_error(error_message) is True - def test_is_idempotent_error_negative(self): """Test that non-idempotent errors are not detected as idempotent errors""" error_message = "Database error code: 42501 - permission denied"