mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-05 04:22:19 +00:00
[Fix] Malformed messages returning 500 instead of 400
The existing AttributeError detection in proxy error handling only checked one level deep in the exception chain (__cause__, __context__, original_exception). In practice, the AttributeError from malformed messages gets wrapped in multiple layers (AttributeError -> OpenAIException -> APIConnectionError), so the check never found it. Extracted the check into _has_attribute_error_in_chain() which walks the full exception chain recursively (depth-capped at 10 to prevent infinite loops from circular references). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
8882b61296
commit
8ca744036a
@@ -15,6 +15,7 @@ from litellm.proxy.common_request_processing import (
|
||||
ProxyConfig,
|
||||
_extract_error_from_sse_chunk,
|
||||
_get_cost_breakdown_from_logging_obj,
|
||||
_has_attribute_error_in_chain,
|
||||
_is_azure_model_router_request,
|
||||
_override_openai_response_model,
|
||||
_parse_event_data_for_error,
|
||||
@@ -1701,3 +1702,50 @@ class TestDDSpanTaggerTagRequest:
|
||||
)
|
||||
|
||||
mock_set_tag.assert_called_once_with("litellm.requested_model", "claude-3-5-sonnet")
|
||||
|
||||
|
||||
class TestHasAttributeErrorInChain:
|
||||
"""Tests for _has_attribute_error_in_chain helper."""
|
||||
|
||||
def test_direct_attribute_error(self):
|
||||
exc = AttributeError("'str' object has no attribute 'get'")
|
||||
assert _has_attribute_error_in_chain(exc) is True
|
||||
|
||||
def test_no_attribute_error(self):
|
||||
exc = ValueError("some other error")
|
||||
assert _has_attribute_error_in_chain(exc) is False
|
||||
|
||||
def test_attribute_error_in_cause(self):
|
||||
inner = AttributeError("bad attribute")
|
||||
outer = RuntimeError("wrapper")
|
||||
outer.__cause__ = inner
|
||||
assert _has_attribute_error_in_chain(outer) is True
|
||||
|
||||
def test_attribute_error_in_context(self):
|
||||
inner = AttributeError("bad attribute")
|
||||
outer = RuntimeError("wrapper")
|
||||
outer.__context__ = inner
|
||||
assert _has_attribute_error_in_chain(outer) is True
|
||||
|
||||
def test_attribute_error_in_original_exception(self):
|
||||
inner = AttributeError("bad attribute")
|
||||
outer = RuntimeError("wrapper")
|
||||
outer.original_exception = inner # type: ignore
|
||||
assert _has_attribute_error_in_chain(outer) is True
|
||||
|
||||
def test_attribute_error_nested_two_levels(self):
|
||||
"""Simulates the real failure: AttributeError -> OpenAIException -> APIConnectionError."""
|
||||
attr_err = AttributeError("'str' object has no attribute 'get'")
|
||||
mid = Exception("OpenAIException wrapper")
|
||||
mid.__context__ = attr_err
|
||||
outer = Exception("APIConnectionError wrapper")
|
||||
outer.__context__ = mid
|
||||
assert _has_attribute_error_in_chain(outer) is True
|
||||
|
||||
def test_depth_limit_prevents_infinite_loop(self):
|
||||
"""Ensure circular references don't cause infinite recursion."""
|
||||
exc_a = RuntimeError("a")
|
||||
exc_b = RuntimeError("b")
|
||||
exc_a.__context__ = exc_b
|
||||
exc_b.__context__ = exc_a # circular
|
||||
assert _has_attribute_error_in_chain(exc_a) is False
|
||||
|
||||
Reference in New Issue
Block a user