Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions litellm/proxy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5156,11 +5156,12 @@ def handle_exception_on_proxy(e: Exception) -> ProxyException:
)
elif isinstance(e, ProxyException):
return e
_status_code = getattr(e, "status_code", status.HTTP_500_INTERNAL_SERVER_ERROR)
return ProxyException(
message="Internal Server Error, " + str(e),
message=str(e),
type=ProxyErrorTypes.internal_server_error,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 type field remains internal_server_error for non-5xx errors

The code (HTTP status) is now correctly forwarded (e.g., 429 for rate limits, 401 for auth errors), but type is still hardcoded to ProxyErrorTypes.internal_server_error. This means the JSON response body for a rate-limit error will contain {"error": {"type": "internal_server_error", "code": "429"}}, which is semantically inconsistent.

The OpenAI API returns "type": "requests" for 429s. Clients that inspect the type field to determine error category will get incorrect information.

A follow-up improvement could derive a more appropriate ProxyErrorTypes from the exception class (e.g. a rate_limit_error enum value), though that's a larger refactor than this PR's scope.

param=getattr(e, "param", "None"),
code=status.HTTP_500_INTERNAL_SERVER_ERROR,
code=_status_code,
)


Expand Down
47 changes: 47 additions & 0 deletions tests/proxy_unit_tests/test_proxy_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2585,3 +2585,50 @@ async def test_handle_logging_proxy_only_error_skips_handlers_for_pass_through()
mock_async.assert_not_called()
mock_sync.assert_not_called()
assert logging_obj.call_type == CallTypes.pass_through.value


def test_handle_exception_on_proxy_preserves_status_code():
"""
OpenAI batch creation returns 429 for rate limits. LiteLLM wraps this as a
RateLimitError with status_code=429. handle_exception_on_proxy must pass
that status code through instead of hardcoding 500.
"""
from litellm.proxy.utils import handle_exception_on_proxy

rate_limit_error = litellm.RateLimitError(
message="Rate limit exceeded: batch creation limit of 2000/hour hit",
llm_provider="openai",
model="gpt-4o",
)

result = handle_exception_on_proxy(rate_limit_error)

assert int(result.code) == 429, f"Expected 429, got {result.code}"


def test_handle_exception_on_proxy_defaults_to_500_for_unknown_exceptions():
"""
Generic exceptions with no status_code should still return 500.
"""
from litellm.proxy.utils import handle_exception_on_proxy

result = handle_exception_on_proxy(Exception("something went wrong"))

assert int(result.code) == 500, f"Expected 500, got {result.code}"


def test_handle_exception_on_proxy_preserves_auth_error_status_code():
"""
AuthenticationError (401) should also pass through correctly.
"""
from litellm.proxy.utils import handle_exception_on_proxy

auth_error = litellm.AuthenticationError(
message="Invalid API key",
llm_provider="openai",
model="gpt-4o",
)

result = handle_exception_on_proxy(auth_error)

assert int(result.code) == 401, f"Expected 401, got {result.code}"
Loading