From 7d6a080d3f91229ab98e58fafd1aae439928d584 Mon Sep 17 00:00:00 2001 From: Mateo Wang <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 6 Jul 2026 22:45:25 -0700 Subject: [PATCH] fix(responses): surface upstream error status on get instead of 500 (#32287) --- .../exception_mapping_utils.py | 10 +- litellm/llms/custom_httpx/llm_http_handler.py | 7 +- .../test_exception_mapping_utils.py | 19 ++++ .../custom_httpx/test_llm_http_handler.py | 92 +++++++++++++++++++ 4 files changed, 125 insertions(+), 3 deletions(-) diff --git a/litellm/litellm_core_utils/exception_mapping_utils.py b/litellm/litellm_core_utils/exception_mapping_utils.py index d908c5d6f20..9dc202c4717 100644 --- a/litellm/litellm_core_utils/exception_mapping_utils.py +++ b/litellm/litellm_core_utils/exception_mapping_utils.py @@ -1944,7 +1944,7 @@ def _map_azure_exception( response=getattr(original_exception, "response", None), body=getattr(original_exception, "body", None), ) - elif "invalid_request_error" in error_str: + elif "invalid_request_error" in error_str and getattr(original_exception, "status_code", None) in (None, 400): raise BadRequestError( message=f"AzureException BadRequestError - {message}", llm_provider="azure", @@ -1986,6 +1986,14 @@ def _map_azure_exception( litellm_debug_info=extra_information, response=getattr(original_exception, "response", None), ) + elif original_exception.status_code == 404: + raise NotFoundError( + message=f"AzureException NotFoundError - {message}", + llm_provider="azure", + model=model, + litellm_debug_info=extra_information, + response=getattr(original_exception, "response", None), + ) elif original_exception.status_code == 408: raise Timeout( message=f"AzureException Timeout - {message}", diff --git a/litellm/llms/custom_httpx/llm_http_handler.py b/litellm/llms/custom_httpx/llm_http_handler.py index 6b3f5beb37d..c426714a1bd 100644 --- a/litellm/llms/custom_httpx/llm_http_handler.py +++ b/litellm/llms/custom_httpx/llm_http_handler.py @@ -2912,6 +2912,7 @@ class BaseLLMHTTPHandler: try: response = sync_httpx_client.get(url=url, headers=headers, params=data) + response.raise_for_status() except Exception as e: raise self._handle_error( e=e, @@ -2983,9 +2984,9 @@ class BaseLLMHTTPHandler: try: response = await async_httpx_client.get(url=url, headers=headers, params=data) - + response.raise_for_status() except Exception as e: - verbose_logger.exception(f"Error retrieving response: {e}") + verbose_logger.debug(f"Error retrieving response: {e}") raise self._handle_error( e=e, provider_config=responses_api_provider_config, @@ -3076,6 +3077,7 @@ class BaseLLMHTTPHandler: try: response = sync_httpx_client.get(url=url, headers=headers, params=params) + response.raise_for_status() except Exception as e: raise self._handle_error(e=e, provider_config=responses_api_provider_config) @@ -3149,6 +3151,7 @@ class BaseLLMHTTPHandler: try: response = await async_httpx_client.get(url=url, headers=headers, params=params) + response.raise_for_status() except Exception as e: raise self._handle_error(e=e, provider_config=responses_api_provider_config) diff --git a/tests/test_litellm/litellm_core_utils/test_exception_mapping_utils.py b/tests/test_litellm/litellm_core_utils/test_exception_mapping_utils.py index 234d04ec481..f441270be7c 100644 --- a/tests/test_litellm/litellm_core_utils/test_exception_mapping_utils.py +++ b/tests/test_litellm/litellm_core_utils/test_exception_mapping_utils.py @@ -649,3 +649,22 @@ def test_upstream_4xx_without_model_maps_to_bad_request(): assert excinfo.value.status_code == 400 assert "Cannot cancel a synchronous response." in excinfo.value.message + + +def test_azure_404_with_invalid_request_error_type_maps_to_not_found(): + from litellm.llms.base_llm.chat.transformation import BaseLLMException + + original_exception = BaseLLMException( + status_code=404, + message='{"error": {"message": "Response with id \'resp_abc\' not found.", "type": "invalid_request_error"}}', + ) + + with pytest.raises(litellm.NotFoundError) as excinfo: + exception_type( + model=None, + original_exception=original_exception, + custom_llm_provider="azure", + ) + + assert excinfo.value.status_code == 404 + assert "Response with id 'resp_abc' not found." in excinfo.value.message diff --git a/tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py b/tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py index f7808d23858..10539dd2fab 100644 --- a/tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py +++ b/tests/test_litellm/llms/custom_httpx/test_llm_http_handler.py @@ -1745,3 +1745,95 @@ def test_sync_retrieve_file_content_raises_on_http_error(): ) assert exc_info.value.status_code == 404 + + +_UPSTREAM_NOT_FOUND_BODY = { + "error": { + "message": "Response with id 'resp_abc' not found.", + "type": "invalid_request_error", + "param": None, + "code": None, + } +} + + +def _async_handler_returning(status_code: int, body: dict) -> AsyncHTTPHandler: + handler = AsyncHTTPHandler() + handler.client = httpx.AsyncClient( + transport=httpx.MockTransport(lambda request: httpx.Response(status_code, json=body)) + ) + return handler + + +def _sync_handler_returning(status_code: int, body: dict) -> HTTPHandler: + handler = HTTPHandler() + handler.client = httpx.Client(transport=httpx.MockTransport(lambda request: httpx.Response(status_code, json=body))) + return handler + + +@pytest.mark.asyncio +async def test_aget_responses_surfaces_upstream_error_status_instead_of_500(): + client = _async_handler_returning(404, _UPSTREAM_NOT_FOUND_BODY) + + with pytest.raises(litellm.NotFoundError) as excinfo: + await litellm.aget_responses( + response_id="resp_abc", + custom_llm_provider="azure", + api_base="https://test.openai.azure.com", + api_key="test-key", + api_version="2025-03-01-preview", + client=client, + ) + + assert excinfo.value.status_code == 404 + assert "Response with id 'resp_abc' not found." in excinfo.value.message + + +def test_get_responses_surfaces_upstream_error_status_instead_of_500(): + client = _sync_handler_returning(404, _UPSTREAM_NOT_FOUND_BODY) + + with pytest.raises(litellm.NotFoundError) as excinfo: + litellm.get_responses( + response_id="resp_abc", + custom_llm_provider="azure", + api_base="https://test.openai.azure.com", + api_key="test-key", + api_version="2025-03-01-preview", + client=client, + ) + + assert excinfo.value.status_code == 404 + assert "Response with id 'resp_abc' not found." in excinfo.value.message + + +def test_list_input_items_surfaces_upstream_error_status(): + client = _sync_handler_returning(404, _UPSTREAM_NOT_FOUND_BODY) + + with pytest.raises(litellm.NotFoundError) as excinfo: + litellm.list_input_items( + response_id="resp_abc", + custom_llm_provider="azure", + api_base="https://test.openai.azure.com", + api_key="test-key", + api_version="2025-03-01-preview", + client=client, + ) + + assert excinfo.value.status_code == 404 + + +@pytest.mark.asyncio +async def test_alist_input_items_surfaces_upstream_error_status(): + client = _async_handler_returning(404, _UPSTREAM_NOT_FOUND_BODY) + + with pytest.raises(litellm.NotFoundError) as excinfo: + await litellm.alist_input_items( + response_id="resp_abc", + custom_llm_provider="azure", + api_base="https://test.openai.azure.com", + api_key="test-key", + api_version="2025-03-01-preview", + client=client, + ) + + assert excinfo.value.status_code == 404