diff --git a/litellm/proxy/common_request_processing.py b/litellm/proxy/common_request_processing.py index 97f7d51970c..b6483b5e563 100644 --- a/litellm/proxy/common_request_processing.py +++ b/litellm/proxy/common_request_processing.py @@ -239,10 +239,38 @@ async def _parse_event_data_for_error(event_line: Union[str, bytes]) -> Optional # Not a valid integer string, treat as if no valid code was found for this check pass - # Ensure error_code is a valid HTTP status code - if error_code is not None and 100 <= error_code <= 599: - return error_code - elif error_code_raw is not None: # Log if original code was present but not valid + if error_code is not None: + if 100 <= error_code <= 599: + # Standard HTTP status code + return error_code + elif error_code >= 600: + # Vendor-specific error codes outside the standard HTTP + # range (e.g., ZAI/ZhipuAI 1302 rate limit, DashScope + # throttling codes). Previously these were silently + # dropped (returned None), causing the proxy to treat + # the SSE chunk as normal content — the error was + # forwarded to the client as a 200 response, and router + # fallback/cooldown never triggered. + # + # Map to 502 (Bad Gateway) so that failure handling + # (retry, fallback, cooldown) fires correctly. The + # original vendor code is preserved in the warning log + # for debugging. + verbose_proxy_logger.warning( + f"SSE error code {error_code} is outside standard HTTP range " + f"(100-599); mapping to 502 for failure handling" + ) + return 502 + else: + # Integer code below 100 (or negative) — likely corrupt data + # but keep the original behaviour of returning None while + # emitting the diagnostic warning (preserved from the + # original elif error_code_raw is not None: branch). + verbose_proxy_logger.warning(f"Error has invalid or non-convertible code: {error_code}") + elif error_code_raw is not None: + # error_code could not be coerced to int from the raw value + # (e.g., a non-numeric string). Log the original raw value + # for debugging and fall through to return None. verbose_proxy_logger.warning(f"Error has invalid or non-convertible code: {error_code_raw}") except (orjson.JSONDecodeError, json.JSONDecodeError): # not a known error chunk diff --git a/tests/test_litellm/proxy/test_common_request_processing.py b/tests/test_litellm/proxy/test_common_request_processing.py index 1d0dafed171..5a3db54520d 100644 --- a/tests/test_litellm/proxy/test_common_request_processing.py +++ b/tests/test_litellm/proxy/test_common_request_processing.py @@ -1118,11 +1118,15 @@ class TestCommonRequestProcessingHelpers: ( 'data: {"error": {"code": 99, "message": "too low"}}', None, - ), # Integer code too low + ), # Integer code too low (< 100, likely invalid data) ( - 'data: {"error": {"code": 600, "message": "too high"}}', - None, - ), # Integer code too high + 'data: {"error": {"code": 600, "message": "vendor error"}}', + 502, + ), # Non-standard code >= 600 → mapped to 502 for failure handling + ( + 'data: {"error": {"code": 1302, "message": "rate limit"}}', + 502, + ), # Vendor-specific rate limit code (ZAI/ZhipuAI) → mapped to 502 ( 'data: {"id": "123", "content": "hello"}', None,