From 5bbf1452b377e28255c70c492849c010f4b4a774 Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Thu, 20 Aug 2026 20:27:51 -0700 Subject: [PATCH] docs(key budgets): state why a swallowed lookup reports unlimited, not unknown Several of the helpers this reads entities through swallow a read error and hand back an absence instead of raising. Enforcement calls those same helpers and acts on that same absence, so a scope reported as unlimited is one no check will apply on the request the row describes. _Unavailable is for the helpers that do raise, whose failure leaves a budget that would still gate the request, and those report unknown. Pins both halves: a tag whose fetch failed open, and a team whose lookup raised. --- .../key_budget_resolver.py | 6 +++- .../test_key_management_endpoints.py | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/management_endpoints/key_budget_resolver.py b/litellm/proxy/management_endpoints/key_budget_resolver.py index 27e67ad1c8e..376094eab08 100644 --- a/litellm/proxy/management_endpoints/key_budget_resolver.py +++ b/litellm/proxy/management_endpoints/key_budget_resolver.py @@ -7,7 +7,11 @@ live spend, and the operator the enforcing check compares with. The limit and counter for each scope come from the same helpers enforcement uses, so the two cannot disagree about which budget applies or which counter it is measured -against. +against. That holds through failure too: several of those helpers swallow a read +error and hand back an absence rather than raising, and since enforcement acts on +that same absence, a scope this reports as unlimited is one no check will apply on +the request it describes. ``_Unavailable`` is for the helpers that do raise, whose +failures leave a budget that would still be enforced, and those become ``unknown``. """ import asyncio diff --git a/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py index 7e09ce48e41..e31524f9d53 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_key_management_endpoints.py @@ -17326,3 +17326,32 @@ async def test_key_budgets_route_shows_the_proxy_numbers_only_to_a_proxy_wide_re assert response.status_code == 200 assert resolver.await_args.kwargs["deps"].proxy_spend_visible is expected + + +@pytest.mark.asyncio +async def test_key_budgets_report_a_tag_its_own_fetch_fails_open_on_the_way_the_request_will_see_it(): + """ + `_fetch_uncached_tags` swallows a failed tag fetch into "no budget objects" so auth never breaks + on it, and the enforcing check reads that same empty result. Reporting the tag as unknown would + name a limit that will not be applied to the request this row describes. + """ + world = _fully_populated_world(tags={}) + with _budgets_world(**world): + budgets = await resolve_key_budgets(valid_token=_budgets_token(), end_user_id=None, deps=_budgets_deps()) + + entry = next(e for e in budgets if e.scope == "tag") + assert (entry.max_budget, entry.status) == (None, "unlimited") + + +@pytest.mark.asyncio +async def test_key_budgets_report_a_scope_whose_helper_raises_as_unknown_rather_than_unlimited(): + """`get_team_object` raises rather than swallowing, so a request would still be gated on the team.""" + + async def _explode(**kwargs): + raise RuntimeError("team lookup is down") + + with _budgets_world(**_fully_populated_world()), patch(f"{_BUDGETS_RESOLVER}.get_team_object", _explode): + budgets = await resolve_key_budgets(valid_token=_budgets_token(), end_user_id=None, deps=_budgets_deps()) + + entry = next(e for e in budgets if e.scope == "team") + assert (entry.max_budget, entry.spend, entry.status) == (None, None, "unknown")