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():