From 46390b508492abcd767d82b15b7814be72aa7922 Mon Sep 17 00:00:00 2001 From: Richard Guo Date: Thu, 2 Apr 2026 17:15:35 +0000 Subject: [PATCH] fix(auth): add missing await in get_org_object() cache lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `get_org_object()` at auth_checks.py:2440 was calling `user_api_key_cache.async_get_cache()` without `await`, returning a coroutine object instead of the cached value. Since a coroutine is always truthy, `cached_org_obj is not None` was always True, but the subsequent `isinstance` checks against dict/LiteLLM_OrganizationTable always failed, causing every org lookup to fall through to the database. This is the only call site missing `await` — all 14 other calls to `async_get_cache` in the same file correctly use `await`. Added a regression test that verifies cache hits return the org object directly without hitting the database. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- litellm/proxy/auth/auth_checks.py | 2 +- .../proxy/auth/test_auth_checks.py | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/auth/auth_checks.py b/litellm/proxy/auth/auth_checks.py index 68bde8434a6..ee3ce9caba1 100644 --- a/litellm/proxy/auth/auth_checks.py +++ b/litellm/proxy/auth/auth_checks.py @@ -2437,7 +2437,7 @@ async def get_org_object( cache_key = "org_id:{}:with_budget".format(org_id) # check if in cache - cached_org_obj = user_api_key_cache.async_get_cache(key=cache_key) + cached_org_obj = await user_api_key_cache.async_get_cache(key=cache_key) if cached_org_obj is not None: if isinstance(cached_org_obj, dict): return LiteLLM_OrganizationTable(**cached_org_obj) diff --git a/tests/test_litellm/proxy/auth/test_auth_checks.py b/tests/test_litellm/proxy/auth/test_auth_checks.py index bd659ed518f..b81b5fdb1db 100644 --- a/tests/test_litellm/proxy/auth/test_auth_checks.py +++ b/tests/test_litellm/proxy/auth/test_auth_checks.py @@ -38,6 +38,7 @@ from litellm.proxy.auth.auth_checks import ( _virtual_key_max_budget_check, _virtual_key_soft_budget_check, get_key_object, + get_org_object, get_user_object, vector_store_access_check, ) @@ -1780,3 +1781,40 @@ async def test_team_member_budget_check_reads_from_spend_counter(): proxy_logging_obj=proxy_logging_obj, ) assert exc_info.value.current_cost == 1.5 + + +@pytest.mark.asyncio +async def test_get_org_object_returns_cached_org(): + """ + Verify that get_org_object() correctly returns a cached organization object + instead of falling through to a DB query. Regression test for a missing + `await` on async_get_cache that caused the cache to be bypassed on every call. + """ + from litellm.proxy._types import LiteLLM_OrganizationTable + + cached_org = LiteLLM_OrganizationTable( + organization_id="test-org-123", + organization_alias="Test Org", + budget_id="budget-1", + models=["gpt-4"], + created_by="admin", + updated_by="admin", + ) + + mock_cache = AsyncMock() + mock_cache.async_get_cache = AsyncMock(return_value=cached_org) + + mock_prisma = MagicMock() + # If cache works, find_unique should never be called + mock_prisma.db.litellm_organizationtable.find_unique = AsyncMock() + + result = await get_org_object( + org_id="test-org-123", + prisma_client=mock_prisma, + user_api_key_cache=mock_cache, + ) + + assert result is not None + assert result.organization_id == "test-org-123" + mock_cache.async_get_cache.assert_awaited_once() + mock_prisma.db.litellm_organizationtable.find_unique.assert_not_awaited()