diff --git a/litellm/proxy/auth/auth_checks.py b/litellm/proxy/auth/auth_checks.py index 359bb944546..7ae906e7a7d 100644 --- a/litellm/proxy/auth/auth_checks.py +++ b/litellm/proxy/auth/auth_checks.py @@ -75,6 +75,23 @@ db_cache_expiry = DEFAULT_IN_MEMORY_TTL # refresh every 5s all_routes = LiteLLMRoutes.openai_routes.value + LiteLLMRoutes.management_routes.value +def _log_budget_lookup_failure(entity: str, error: Exception) -> None: + """ + Log a warning when budget lookup fails; cache will not be populated. + + Adds a schema migration hint when the error appears schema-related. + """ + err_str = str(error).lower() + hint = "" + if any( + x in err_str + for x in ("column", "schema", "does not exist", "prisma", "migrate") + ): + hint = " Run `prisma db push` or `prisma migrate deploy` to fix schema mismatches." + verbose_proxy_logger.error( + f"Budget lookup failed for {entity}; cache will not be populated. " + f"Each request will hit the database. Error: {error}.{hint}" + ) def _is_model_cost_zero( model: Optional[Union[str, List[str]]], llm_router: Optional[Router] @@ -1208,6 +1225,7 @@ async def get_user_object( return _response except Exception as e: # if user not in db + _log_budget_lookup_failure("user", e) raise ValueError( f"User doesn't exist in db. 'user_id'={user_id}. Create user via `/user/new` call. Got error - {e}" ) diff --git a/tests/test_litellm/proxy/auth/test_auth_checks.py b/tests/test_litellm/proxy/auth/test_auth_checks.py index ebcd9676129..7ca3696302a 100644 --- a/tests/test_litellm/proxy/auth/test_auth_checks.py +++ b/tests/test_litellm/proxy/auth/test_auth_checks.py @@ -30,6 +30,7 @@ from litellm.proxy.auth.auth_checks import ( _can_object_call_vector_stores, _get_fuzzy_user_object, _get_team_db_check, + _log_budget_lookup_failure, _virtual_key_max_budget_alert_check, _virtual_key_soft_budget_check, get_user_object, @@ -273,6 +274,19 @@ async def test_default_internal_user_params_with_get_user_object(monkeypatch): assert creation_args["user_role"] == "internal_user" +def test_log_budget_lookup_failure_dry_run(): + """Dry run: verify _log_budget_lookup_failure logs without raising.""" + with patch("litellm.proxy.auth.auth_checks.verbose_proxy_logger") as mock_logger: + err = Exception("column 'policies' does not exist in prisma schema") + _log_budget_lookup_failure("user", err) + mock_logger.error.assert_called_once() + call_msg = mock_logger.error.call_args[0][0] + assert "user" in call_msg + assert "cache will not be populated" in call_msg + assert "policies" in call_msg or "prisma" in call_msg + assert "prisma db push" in call_msg + + @pytest.mark.asyncio @patch("litellm.proxy.management_endpoints.team_endpoints.new_team", new_callable=AsyncMock) async def test_get_team_db_check_calls_new_team_on_upsert(mock_new_team, monkeypatch):