From 49c7604ed81edb186ca8c43f047607a3c937a203 Mon Sep 17 00:00:00 2001 From: suleimanelkhoury Date: Mon, 1 Jun 2026 15:19:34 +0200 Subject: [PATCH] fix(proxy): remove premature end-user budget check from get_end_user_object Problem: - `_check_end_user_budget()` was called inside `get_end_user_object()` - This caused budget checks to run BEFORE `skip_budget_checks` could be evaluated - Zero-cost models (e.g., local vLLM) were incorrectly blocked when end-users exceeded their budget, even though they should bypass budget checks Solution: - Remove `_check_end_user_budget()` calls from `get_end_user_object()` - Budget enforcement now happens exclusively in `common_checks()` where `skip_budget_checks` context is available - `get_end_user_object()` keeps `route` as optional in function parameter for backwards compatibility and future implementation. --- litellm/proxy/auth/auth_checks.py | 14 ++------------ litellm/proxy/auth/user_api_key_auth.py | 3 +-- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/litellm/proxy/auth/auth_checks.py b/litellm/proxy/auth/auth_checks.py index 38976f79aa3..a79e9c80e51 100644 --- a/litellm/proxy/auth/auth_checks.py +++ b/litellm/proxy/auth/auth_checks.py @@ -1109,7 +1109,7 @@ async def get_end_user_object( end_user_id: Optional[str], prisma_client: Optional[PrismaClient], user_api_key_cache: UserApiKeyCache, - route: str, + route: Optional[str] = "", parent_otel_span: Optional[Span] = None, proxy_logging_obj: Optional[ProxyLogging] = None, ) -> Optional[LiteLLM_EndUserTable]: @@ -1153,9 +1153,6 @@ async def get_end_user_object( parent_otel_span=parent_otel_span, ) - # Check budget limits - await _check_end_user_budget(end_user_obj=return_obj, route=route) - return return_obj # Fetch from database @@ -1186,14 +1183,9 @@ async def get_end_user_object( model_type=LiteLLM_EndUserTable, ) - # Check budget limits - await _check_end_user_budget(end_user_obj=_response, route=route) - return _response - except Exception as e: - if isinstance(e, litellm.BudgetExceededError): - raise e + except Exception: return None @@ -1290,8 +1282,6 @@ async def _end_user_id_exists_in_db( ) if end_user_obj is not None: return True - except litellm.BudgetExceededError: - raise except Exception as e: verbose_proxy_logger.debug( f"end_user validation: get_end_user_object lookup failed: {e}" diff --git a/litellm/proxy/auth/user_api_key_auth.py b/litellm/proxy/auth/user_api_key_auth.py index 2f9a1411400..6313fd108c2 100644 --- a/litellm/proxy/auth/user_api_key_auth.py +++ b/litellm/proxy/auth/user_api_key_auth.py @@ -1757,8 +1757,7 @@ async def _user_api_key_auth_builder( # noqa: PLR0915 async def _safe_fetch(label: str, awaitable): """Run an awaitable and return its result. Re-raises authentication / authorization failures (HTTPException, ProxyException, - BudgetExceededError — which ``get_end_user_object`` raises for - end-user budget violations) so they propagate to the caller. + BudgetExceededError) so they propagate to the caller. Other exceptions (e.g. transient DB errors fetching context) are swallowed with a debug log and ``None`` is returned so ``common_checks`` can still run against whatever limits are recorded