From c61ec511087825f6681c6d96dcd01e1f98eeb8a1 Mon Sep 17 00:00:00 2001 From: dingdangmao <2756823972@qq.com> Date: Sun, 8 Nov 2026 10:49:34 +0800 Subject: [PATCH] fix(key/info): return 400 instead of 500 when no key is provided (#43577) * fix(key/info): return 400 instead of 500 when no key is provided When GET /key/info is called without a ?key= query parameter and the authenticated principal has no virtual key (e.g. an admin-UI session token whose UserAPIKeyAuth.api_key is None), info_key_fn passed hashed_key=None to Prisma find_unique(where={"token": None}), which raised MissingRequiredValueError and surfaced as an unhandled 500. Add an explicit None/empty guard that raises a clean 400 ProxyException before the DB lookup, and add a regression test asserting that prisma find_unique is never called with token=None. Fixes #43571 Signed-off-by: apex-mochen <2756823972@qq.com> * test: remove redundant assertion comment * fix(key/info): remove redundant None check to satisfy basedpyright delta gate After if not key: raise, key is narrowed to str. The subsequent if key is not None: guard is always true and triggers basedpyright reportUnnecessaryComparison, which fails the delta gate. Collapse to a single hash_token_if_needed(token=key) call. Signed-off-by: apex-mochen <2756823972@qq.com> --------- Signed-off-by: apex-mochen <2756823972@qq.com> Co-authored-by: ryan-crabbe-berri --- .../key_management_endpoints.py | 11 ++++++++--- .../test_key_management_endpoints.py | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/litellm/proxy/management_endpoints/key_management_endpoints.py b/litellm/proxy/management_endpoints/key_management_endpoints.py index d4a0a35d9eb..d54eda45160 100644 --- a/litellm/proxy/management_endpoints/key_management_endpoints.py +++ b/litellm/proxy/management_endpoints/key_management_endpoints.py @@ -5104,9 +5104,14 @@ async def info_key_fn( # default to using Auth token if no key is passed in key = key or user_api_key_dict.api_key - hashed_key: str | None = key - if key is not None: - hashed_key = hash_token_if_needed(token=key) # rebind-ok: pre-existing rebinding on a rename-only line + if not key: + raise ProxyException( + message="No key passed in. Pass ?key= or authenticate with a virtual key.", + type=ProxyErrorTypes.bad_request_error, + param="key", + code=status.HTTP_400_BAD_REQUEST, + ) + hashed_key: str | None = hash_token_if_needed(token=key) live_key_info: Final = await _prisma_table(VerificationTokenRepository(prisma_client)).find_unique( where={"token": hashed_key}, include={"litellm_budget_table": True}, diff --git a/tests/unit/proxy/management_endpoints/test_key_management_endpoints.py b/tests/unit/proxy/management_endpoints/test_key_management_endpoints.py index 77dc6eddf51..f83d53ac2e5 100644 --- a/tests/unit/proxy/management_endpoints/test_key_management_endpoints.py +++ b/tests/unit/proxy/management_endpoints/test_key_management_endpoints.py @@ -6980,6 +6980,22 @@ async def test_info_key_fn_unknown_key_still_404s(monkeypatch): assert exc_info.value.code == "404" +@pytest.mark.asyncio +async def test_info_key_fn_no_key_returns_400_not_500(monkeypatch): + """#43571: when both ?key= and auth api_key are None, return a clean 400 + instead of letting Prisma raise MissingRequiredValueError as a 500.""" + from litellm.proxy.management_endpoints.key_management_endpoints import info_key_fn + + mock_prisma_client = AsyncMock() + monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client) + + auth = UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN, api_key=None) + with pytest.raises(ProxyException) as exc_info: + await info_key_fn(key=None, user_api_key_dict=auth) + assert exc_info.value.code == "400" + mock_prisma_client.db.litellm_verificationtoken.find_unique.assert_not_called() + + @pytest.mark.asyncio @pytest.mark.parametrize( ("blocked", "expires", "expected_status"),