diff --git a/litellm/litellm_core_utils/exception_mapping_utils.py b/litellm/litellm_core_utils/exception_mapping_utils.py index 91d2f296d0..7a0cffab7b 100644 --- a/litellm/litellm_core_utils/exception_mapping_utils.py +++ b/litellm/litellm_core_utils/exception_mapping_utils.py @@ -422,6 +422,7 @@ def exception_type( # type: ignore # noqa: PLR0915 llm_provider=custom_llm_provider, response=getattr(original_exception, "response", None), litellm_debug_info=extra_information, + body=getattr(original_exception, "body", None), ) elif original_exception.status_code == 429: exception_mapping_worked = True @@ -1961,6 +1962,7 @@ def exception_type( # type: ignore # noqa: PLR0915 model=model, litellm_debug_info=extra_information, response=getattr(original_exception, "response", None), + body=getattr(original_exception, "body", None), ) elif ( "The api_key client option must be set either by passing api_key to the client or by setting" @@ -1992,6 +1994,7 @@ def exception_type( # type: ignore # noqa: PLR0915 model=model, litellm_debug_info=extra_information, response=getattr(original_exception, "response", None), + body=getattr(original_exception, "body", None), ) elif original_exception.status_code == 401: exception_mapping_worked = True diff --git a/litellm/llms/azure/azure.py b/litellm/llms/azure/azure.py index 5294bd7141..dcd5af7b96 100644 --- a/litellm/llms/azure/azure.py +++ b/litellm/llms/azure/azure.py @@ -540,10 +540,14 @@ class AzureChatCompletion(BaseLLM): status_code = getattr(e, "status_code", 500) error_headers = getattr(e, "headers", None) error_response = getattr(e, "response", None) + error_body = getattr(e, "body", None) if error_headers is None and error_response: error_headers = getattr(error_response, "headers", None) raise AzureOpenAIError( - status_code=status_code, message=str(e), headers=error_headers + status_code=status_code, + message=str(e), + headers=error_headers, + body=error_body, ) async def acompletion( @@ -649,6 +653,7 @@ class AzureChatCompletion(BaseLLM): raise AzureOpenAIError(status_code=500, message=str(e)) except Exception as e: message = getattr(e, "message", str(e)) + body = getattr(e, "body", None) ## LOGGING logging_obj.post_call( input=data["messages"], @@ -659,7 +664,7 @@ class AzureChatCompletion(BaseLLM): if hasattr(e, "status_code"): raise e else: - raise AzureOpenAIError(status_code=500, message=message) + raise AzureOpenAIError(status_code=500, message=message, body=body) def streaming( self, @@ -805,10 +810,14 @@ class AzureChatCompletion(BaseLLM): error_headers = getattr(e, "headers", None) error_response = getattr(e, "response", None) message = getattr(e, "message", str(e)) + error_body = getattr(e, "body", None) if error_headers is None and error_response: error_headers = getattr(error_response, "headers", None) raise AzureOpenAIError( - status_code=status_code, message=message, headers=error_headers + status_code=status_code, + message=message, + headers=error_headers, + body=error_body, ) async def aembedding( diff --git a/litellm/llms/azure/common_utils.py b/litellm/llms/azure/common_utils.py index 2a96f5c39c..43f3480ed6 100644 --- a/litellm/llms/azure/common_utils.py +++ b/litellm/llms/azure/common_utils.py @@ -17,6 +17,7 @@ class AzureOpenAIError(BaseLLMException): request: Optional[httpx.Request] = None, response: Optional[httpx.Response] = None, headers: Optional[Union[httpx.Headers, dict]] = None, + body: Optional[dict] = None, ): super().__init__( status_code=status_code, @@ -24,6 +25,7 @@ class AzureOpenAIError(BaseLLMException): request=request, response=response, headers=headers, + body=body, ) diff --git a/tests/llm_translation/test_openai.py b/tests/llm_translation/test_openai.py index d248dd28a3..172c946636 100644 --- a/tests/llm_translation/test_openai.py +++ b/tests/llm_translation/test_openai.py @@ -391,34 +391,3 @@ def test_openai_chat_completion_streaming_handler_reasoning_content(): ) assert response.choices[0].delta.reasoning_content == "." - - -@pytest.mark.parametrize("sync_mode", [True, False]) -@pytest.mark.parametrize("stream_mode", [True, False]) -@pytest.mark.asyncio -async def test_exception_bubbling_up(sync_mode, stream_mode): - """ - make sure code, param, and type are bubbled up - """ - import litellm - - litellm.set_verbose = True - with pytest.raises(Exception) as exc_info: - if sync_mode: - litellm.completion( - model="gpt-4o-mini", - messages=[{"role": "usera", "content": "hi"}], - stream=stream_mode, - sync_stream=sync_mode, - ) - else: - await litellm.acompletion( - model="gpt-4o-mini", - messages=[{"role": "usera", "content": "hi"}], - stream=stream_mode, - sync_stream=sync_mode, - ) - - assert exc_info.value.code == "invalid_value" - assert exc_info.value.param is not None - assert exc_info.value.type == "invalid_request_error" diff --git a/tests/local_testing/test_exceptions.py b/tests/local_testing/test_exceptions.py index 0b4f828054..e68d368779 100644 --- a/tests/local_testing/test_exceptions.py +++ b/tests/local_testing/test_exceptions.py @@ -1205,3 +1205,35 @@ def test_context_window_exceeded_error_from_litellm_proxy(): } with pytest.raises(litellm.ContextWindowExceededError): extract_and_raise_litellm_exception(**args) + + +@pytest.mark.parametrize("sync_mode", [True, False]) +@pytest.mark.parametrize("stream_mode", [True, False]) +@pytest.mark.parametrize("model", ["azure/gpt-4o"]) # "gpt-4o-mini", +@pytest.mark.asyncio +async def test_exception_bubbling_up(sync_mode, stream_mode, model): + """ + make sure code, param, and type are bubbled up + """ + import litellm + + litellm.set_verbose = True + with pytest.raises(Exception) as exc_info: + if sync_mode: + litellm.completion( + model=model, + messages=[{"role": "usera", "content": "hi"}], + stream=stream_mode, + sync_stream=sync_mode, + ) + else: + await litellm.acompletion( + model=model, + messages=[{"role": "usera", "content": "hi"}], + stream=stream_mode, + sync_stream=sync_mode, + ) + + assert exc_info.value.code == "invalid_value" + assert exc_info.value.param is not None + assert exc_info.value.type == "invalid_request_error"