From 41011ed8fee98bf26e9662074f35597cc9f154fe Mon Sep 17 00:00:00 2001 From: yurekami Date: Mon, 29 Dec 2025 07:12:18 +0900 Subject: [PATCH] fix(proxy): use error_message variable in raise_rate_limit_error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The raise_rate_limit_error method constructed an error_message variable correctly but then used an f-string that directly interpolated additional_details, causing "None" to appear in the error message when no additional details were provided. Fixes #18460 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../proxy/hooks/parallel_request_limiter.py | 2 +- .../hooks/test_parallel_request_limiter.py | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 tests/test_litellm/proxy/hooks/test_parallel_request_limiter.py diff --git a/litellm/proxy/hooks/parallel_request_limiter.py b/litellm/proxy/hooks/parallel_request_limiter.py index c7bfc27d6b6..f19174efca8 100644 --- a/litellm/proxy/hooks/parallel_request_limiter.py +++ b/litellm/proxy/hooks/parallel_request_limiter.py @@ -132,7 +132,7 @@ class _PROXY_MaxParallelRequestsHandler(CustomLogger): error_message = error_message + " " + additional_details raise HTTPException( status_code=429, - detail=f"Max parallel request limit reached {additional_details}", + detail=error_message, headers={"retry-after": str(self.time_to_next_minute())}, ) diff --git a/tests/test_litellm/proxy/hooks/test_parallel_request_limiter.py b/tests/test_litellm/proxy/hooks/test_parallel_request_limiter.py new file mode 100644 index 00000000000..99f46aa4a6a --- /dev/null +++ b/tests/test_litellm/proxy/hooks/test_parallel_request_limiter.py @@ -0,0 +1,61 @@ +""" +Unit Tests for the max parallel request limiter for the proxy +""" + +import pytest +from fastapi import HTTPException +from unittest.mock import MagicMock + +from litellm.caching.caching import DualCache +from litellm.proxy.hooks.parallel_request_limiter import ( + _PROXY_MaxParallelRequestsHandler, +) + + +class TestRaiseRateLimitError: + """Tests for the raise_rate_limit_error method""" + + def test_raise_rate_limit_error_without_additional_details(self): + """ + Test that error message does not contain 'None' when additional_details is not provided. + + This is a regression test for issue #18460 where the error message would + show "Max parallel request limit reached None" instead of just + "Max parallel request limit reached". + """ + mock_cache = MagicMock(spec=DualCache) + mock_internal_cache = MagicMock() + mock_internal_cache.dual_cache = mock_cache + + handler = _PROXY_MaxParallelRequestsHandler( + internal_usage_cache=mock_internal_cache + ) + + with pytest.raises(HTTPException) as exc_info: + handler.raise_rate_limit_error() + + assert exc_info.value.status_code == 429 + assert exc_info.value.detail == "Max parallel request limit reached" + assert "None" not in exc_info.value.detail + assert "retry-after" in exc_info.value.headers + + def test_raise_rate_limit_error_with_additional_details(self): + """ + Test that error message includes additional_details when provided. + """ + mock_cache = MagicMock(spec=DualCache) + mock_internal_cache = MagicMock() + mock_internal_cache.dual_cache = mock_cache + + handler = _PROXY_MaxParallelRequestsHandler( + internal_usage_cache=mock_internal_cache + ) + + additional_info = "Hit limit for api_key. Current limits: max_parallel_requests: 5" + + with pytest.raises(HTTPException) as exc_info: + handler.raise_rate_limit_error(additional_details=additional_info) + + assert exc_info.value.status_code == 429 + assert exc_info.value.detail == f"Max parallel request limit reached {additional_info}" + assert "retry-after" in exc_info.value.headers