mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
fix(proxy): return 401 not 500 on auth failure in master-key-only mode
On a master-key-only proxy (no DATABASE_URL) the optional `prisma` dependency is never installed. `PrismaDBExceptionHandler`'s classifiers (`is_database_connection_error`, `is_prisma_data_error`, `is_database_transport_error`, `is_prisma_engine_internal_error`) did an unconditional `import prisma` in the method body. These run on every auth failure via `_user_api_key_auth_builder`, so any missing/invalid key crashed the classifier with ModuleNotFoundError and surfaced a 500 instead of a 401. Guard the import once at module load (`PRISMA_AVAILABLE`) and short-circuit each classifier to False when prisma is unavailable. Behavior is unchanged when prisma is installed. Fixes #35457
This commit is contained in:
parent
2b3070890a
commit
7911305e29
2 changed files with 54 additions and 4 deletions
|
|
@ -8,6 +8,16 @@ from litellm.proxy._types import (
|
|||
)
|
||||
from litellm.secret_managers.main import str_to_bool
|
||||
|
||||
try:
|
||||
import prisma # optional dependency, only installed when the proxy is generated against a DATABASE_URL
|
||||
|
||||
PRISMA_AVAILABLE = True
|
||||
except ImportError:
|
||||
# Master-key-only deployments run without a DATABASE_URL and never install
|
||||
# prisma. The classifiers below must not crash the auth-failure path in that
|
||||
# mode, so they short-circuit to False when prisma is unavailable.
|
||||
PRISMA_AVAILABLE = False
|
||||
|
||||
# Bounds the __cause__/__context__ walk in is_database_service_unavailable_error_in_chain.
|
||||
# Real exception chains are a few links deep; the cap also makes the walk cycle-safe.
|
||||
_MAX_EXCEPTION_CHAIN_DEPTH = 20
|
||||
|
|
@ -47,7 +57,10 @@ class PrismaDBExceptionHandler:
|
|||
to True so genuine outages that don't match a specific subclass
|
||||
still trigger the fallback.
|
||||
"""
|
||||
import prisma
|
||||
if not PRISMA_AVAILABLE:
|
||||
# No DATABASE_URL / prisma not installed: there is no DB layer to
|
||||
# be unavailable, so this cannot be a DB-connectivity failure.
|
||||
return False
|
||||
|
||||
# Explicit data-layer exclusion: DB IS reachable, fallback must
|
||||
# NOT fire.
|
||||
|
|
@ -89,7 +102,8 @@ class PrismaDBExceptionHandler:
|
|||
per-row data rejection has to additionally consult
|
||||
``is_database_service_unavailable_error`` before acting on a True here.
|
||||
"""
|
||||
import prisma
|
||||
if not PRISMA_AVAILABLE:
|
||||
return False
|
||||
|
||||
return type(e) is prisma.errors.DataError
|
||||
|
||||
|
|
@ -102,7 +116,8 @@ class PrismaDBExceptionHandler:
|
|||
Use this for reconnect logic — data-layer errors like UniqueViolationError
|
||||
mean the DB IS reachable, so reconnecting would be pointless.
|
||||
"""
|
||||
import prisma
|
||||
if not PRISMA_AVAILABLE:
|
||||
return False
|
||||
|
||||
if isinstance(e, DB_CONNECTION_ERROR_TYPES):
|
||||
return True
|
||||
|
|
@ -154,7 +169,10 @@ class PrismaDBExceptionHandler:
|
|||
are already classified by type/keyword above, and data-layer ones
|
||||
(the DB IS reachable) must stay 401.
|
||||
"""
|
||||
import prisma
|
||||
if not PRISMA_AVAILABLE:
|
||||
# No prisma engine in play, so no prisma-engine-internal error is
|
||||
# possible.
|
||||
return False
|
||||
|
||||
if isinstance(e, prisma.errors.PrismaError):
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -426,3 +426,35 @@ def test_handle_db_exception_with_non_db_error():
|
|||
)
|
||||
with pytest.raises(litellm.BudgetExceededError):
|
||||
PrismaDBExceptionHandler.handle_db_exception(regular_error)
|
||||
|
||||
|
||||
# Regression test for https://github.com/BerriAI/litellm/issues/35457
|
||||
#
|
||||
# On a master-key-only proxy (no DATABASE_URL) the optional `prisma` dependency
|
||||
# is never installed. These classifiers are reached on EVERY auth failure via
|
||||
# `_user_api_key_auth_builder`, so an unconditional `import prisma` inside them
|
||||
# raised ModuleNotFoundError and surfaced a 500 where a 401 was expected. With
|
||||
# prisma unavailable they must instead return False (not a DB error) and not
|
||||
# raise, so auth failures stay 401.
|
||||
@pytest.mark.parametrize(
|
||||
"classifier",
|
||||
[
|
||||
PrismaDBExceptionHandler.is_database_connection_error,
|
||||
PrismaDBExceptionHandler.is_prisma_data_error,
|
||||
PrismaDBExceptionHandler.is_database_transport_error,
|
||||
PrismaDBExceptionHandler.is_prisma_engine_internal_error,
|
||||
],
|
||||
)
|
||||
def test_classifiers_return_false_when_prisma_unavailable(monkeypatch, classifier):
|
||||
"""
|
||||
When prisma is not installed (master-key-only deployment), the classifiers
|
||||
must return False without raising ModuleNotFoundError.
|
||||
"""
|
||||
monkeypatch.setattr(
|
||||
"litellm.proxy.db.exception_handler.PRISMA_AVAILABLE", False
|
||||
)
|
||||
|
||||
# A plain auth failure carries no prisma types; the classifier must handle
|
||||
# it without touching the (absent) prisma module.
|
||||
auth_error = Exception("Authentication Error, invalid api key")
|
||||
assert classifier(auth_error) is False
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue