From 6f71cd46f6748613ab325d8506ccb714f7ce1551 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Sat, 21 Feb 2026 11:55:34 -0800 Subject: [PATCH] fix(proxy): restore broad is_database_connection_error; add is_database_transport_error for reconnect Any PrismaError should be treated as a DB connection error for the allow_requests_on_db_unavailable feature and 503 responses. The narrow keyword-based check is now in is_database_transport_error, which is what the reconnect logic in auth_checks.py should use. Fixes test_delete_access_group_503_on_db_connection_error and test_handle_authentication_error_db_unavailable failures caused by PR #21706 narrowing is_database_connection_error. --- litellm/proxy/auth/auth_checks.py | 2 +- litellm/proxy/db/exception_handler.py | 25 ++++++++++++++++--- .../proxy/db/test_exception_handler.py | 5 ++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/litellm/proxy/auth/auth_checks.py b/litellm/proxy/auth/auth_checks.py index 1fb0133f50b..500a39d9455 100644 --- a/litellm/proxy/auth/auth_checks.py +++ b/litellm/proxy/auth/auth_checks.py @@ -2000,7 +2000,7 @@ async def _fetch_key_object_from_db_with_reconnect( proxy_logging_obj=proxy_logging_obj, ) except Exception as e: - if PrismaDBExceptionHandler.is_database_connection_error(e): + if PrismaDBExceptionHandler.is_database_transport_error(e): did_reconnect = False if hasattr(prisma_client, "attempt_db_reconnect"): auth_reconnect_timeout = getattr( diff --git a/litellm/proxy/db/exception_handler.py b/litellm/proxy/db/exception_handler.py index bbc1564a487..b2efbf9d076 100644 --- a/litellm/proxy/db/exception_handler.py +++ b/litellm/proxy/db/exception_handler.py @@ -32,7 +32,28 @@ class PrismaDBExceptionHandler: @staticmethod def is_database_connection_error(e: Exception) -> bool: """ - Returns True if the exception is from a database outage / connection error + Returns True if the exception is from a database outage / connection error. + Any PrismaError qualifies — the DB failed to serve the request. + Used by allow_requests_on_db_unavailable logic and endpoint 503 responses. + """ + import prisma + + if isinstance(e, DB_CONNECTION_ERROR_TYPES): + return True + if isinstance(e, prisma.errors.PrismaError): + return True + if isinstance(e, ProxyException) and e.type == ProxyErrorTypes.no_db_connection: + return True + return False + + @staticmethod + def is_database_transport_error(e: Exception) -> bool: + """ + Returns True only for transport/connectivity failures where a reconnect + attempt makes sense (e.g. DB is unreachable, connection dropped). + + Use this for reconnect logic — data-layer errors like UniqueViolationError + mean the DB IS reachable, so reconnecting would be pointless. """ import prisma @@ -44,8 +65,6 @@ class PrismaDBExceptionHandler: return True if isinstance(e, prisma.errors.PrismaError): error_message = str(e).lower() - # Treat generic PrismaError as connection error only when its text - # clearly indicates transport/connectivity failure. connection_keywords = ( "can't reach database server", "cannot reach database server", diff --git a/tests/test_litellm/proxy/db/test_exception_handler.py b/tests/test_litellm/proxy/db/test_exception_handler.py index 8c07b2a19e6..9dcf5df4aeb 100644 --- a/tests/test_litellm/proxy/db/test_exception_handler.py +++ b/tests/test_litellm/proxy/db/test_exception_handler.py @@ -72,8 +72,9 @@ def test_is_database_connection_error_prisma_connection_errors(prisma_error): ), ], ) -def test_is_database_connection_error_non_connection_prisma_errors(prisma_error): - assert PrismaDBExceptionHandler.is_database_connection_error(prisma_error) == False +def test_is_database_transport_error_non_connection_prisma_errors(prisma_error): + """Data-layer errors should not trigger reconnect — DB is reachable when these occur.""" + assert PrismaDBExceptionHandler.is_database_transport_error(prisma_error) == False def test_is_database_connection_generic_errors():