Merge pull request #40925 from BerriAI/litellm_proxy_error_headers_from_litellm_response_headers

fix(proxy): forward provider request id headers on mapped error responses
This commit is contained in:
Yassin Kortam 2026-09-14 12:36:59 -07:00 committed by GitHub
commit 7c55c4ba7d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 39 additions and 6 deletions

View file

@ -3452,15 +3452,13 @@ class ProxyBaseLLMRequestProcessing:
# a failed request reports no timing, matching /v1/chat/completions
read_timing_from_logging_obj=False,
)
# Extract headers from exception - check both e.headers and e.response.headers
headers = getattr(e, "headers", None) or {}
if not headers:
# Try to get headers from e.response.headers (httpx.Response)
_response: Final = attribute_of(e, "response")
if _response is not None:
_response_headers: Final = getattr(_response, "headers", None)
if _response_headers:
headers = get_response_headers(dict(_response_headers))
_response_headers: Final = getattr(_response, "headers", None) if _response is not None else None
_provider_headers: Final = _response_headers or getattr(e, "litellm_response_headers", None)
if _provider_headers:
headers = get_response_headers(dict(_provider_headers))
headers.update(custom_headers)
# Call response headers hook for failure

View file

@ -8400,6 +8400,41 @@ async def test_handle_llm_api_exception_forwards_provider_headers_on_http_status
assert exc_info.value.headers["llm_provider-x-amzn-requestid"] == "req-passthrough-500"
@pytest.mark.asyncio
async def test_handle_llm_api_exception_forwards_litellm_response_headers_when_response_is_synthetic():
"""Exception mapping hands the proxy a mapped error whose ``response`` is a synthetic empty
``httpx.Response`` and parks the provider's real headers on ``litellm_response_headers``.
The client must still get the provider request id, as it does on a 200.
"""
import httpx
from litellm.proxy._types import ProxyException, UserAPIKeyAuth
mapped = litellm.BadRequestError(
message="OpenAIException - max_tokens is too large: 999999999.",
model="gpt-4o-mini",
llm_provider="openai",
)
mapped.litellm_response_headers = httpx.Headers({"x-request-id": "req_openai_400"})
assert dict(mapped.response.headers) == {}
processor = ProxyBaseLLMRequestProcessing(data={})
proxy_logging_obj = MagicMock()
proxy_logging_obj.post_call_failure_hook = AsyncMock(return_value=None)
proxy_logging_obj.post_call_response_headers_hook = AsyncMock(return_value={})
with pytest.raises(ProxyException) as exc_info:
await processor._handle_llm_api_exception(
e=mapped,
user_api_key_dict=UserAPIKeyAuth(api_key="sk-test"),
proxy_logging_obj=proxy_logging_obj,
)
assert exc_info.value.code == "400"
assert "max_tokens is too large: 999999999." in exc_info.value.message
assert exc_info.value.headers["llm_provider-x-request-id"] == "req_openai_400"
class TestBackgroundResponseRetrievalGovernance:
"""LIT-7175: retrieving a background Response attaches the model's post_call policy pipelines."""