diff --git a/litellm/proxy/auth/user_api_key_auth.py b/litellm/proxy/auth/user_api_key_auth.py index c44723a4bb9..caa886c0a7c 100644 --- a/litellm/proxy/auth/user_api_key_auth.py +++ b/litellm/proxy/auth/user_api_key_auth.py @@ -2737,12 +2737,17 @@ async def user_api_key_auth_builder( if ( prisma_client is None ): # if both master key + user key submitted, and user key != master key, and no db connected, raise an error - if not PrismaDBExceptionHandler.is_database_configured(): + if ( + not PrismaDBExceptionHandler.is_database_configured() + and PrismaDBExceptionHandler.should_allow_request_on_db_unavailable() + ): # No database was ever configured, so there is no outage to # ride out. ``allow_requests_on_db_unavailable`` must not fail # open here: a non-master key cannot be verified and is # rejected, instead of being admitted via the - # ``no_db_connection`` fallback token. See #47119. + # ``no_db_connection`` fallback token. See #47119. When the + # flag is off no fallback token is issued anyway, so the + # established ``no_db_connection`` error below stands. raise ProxyException( message="Authentication Error - No database configured. Virtual keys require a configured database; only the master key can be used without one.", type=ProxyErrorTypes.auth_error, diff --git a/litellm/proxy/db/exception_handler.py b/litellm/proxy/db/exception_handler.py index 0458af22b7e..3f5d4558716 100644 --- a/litellm/proxy/db/exception_handler.py +++ b/litellm/proxy/db/exception_handler.py @@ -1,5 +1,6 @@ import os import re +import sys from collections.abc import Awaitable, Callable, Iterator from http import HTTPStatus from typing import Final, Protocol, TypeVar @@ -107,14 +108,13 @@ class PrismaDBExceptionHandler: """ if os.getenv("DATABASE_URL") or os.getenv("DIRECT_URL"): return True - try: - from litellm.proxy.proxy_server import general_settings - - if isinstance(general_settings, dict): - return bool(general_settings.get("database_url")) - except Exception: - return False - return False + # Read ``general_settings`` through ``sys.modules``: importing + # ``proxy_server`` here could fail or recurse while it is still + # being imported, and a proxy server that has not been imported + # yet has not configured a database through it either. + proxy_server_module = sys.modules.get("litellm.proxy.proxy_server") + general_settings = getattr(proxy_server_module, "general_settings", None) + return isinstance(general_settings, dict) and bool(general_settings.get("database_url")) @staticmethod def is_database_connection_error(e: Exception) -> bool: diff --git a/tests/unit/proxy/db/test_exception_handler.py b/tests/unit/proxy/db/test_exception_handler.py index f21f2e82748..a0391781cd5 100644 --- a/tests/unit/proxy/db/test_exception_handler.py +++ b/tests/unit/proxy/db/test_exception_handler.py @@ -964,3 +964,9 @@ def test_is_database_configured(monkeypatch): monkeypatch.setenv("DATABASE_URL", "postgresql://user:pass@localhost:5432/litellm") with patch("litellm.proxy.proxy_server.general_settings", {}): assert PrismaDBExceptionHandler.is_database_configured() is True + monkeypatch.delenv("DATABASE_URL", raising=False) + with patch( + "litellm.proxy.proxy_server.general_settings", + {"database_url": "postgresql://localhost:5432/litellm"}, + ): + assert PrismaDBExceptionHandler.is_database_configured() is True