From 8755a90d508ca14157f9fe8bdd6658c6d7b5565f Mon Sep 17 00:00:00 2001 From: Lakshya Dhariwal Date: Sun, 18 Oct 2026 13:01:12 +0530 Subject: [PATCH 1/4] fix(ollama): raise on non-completion 200 response bodies Add error handling for Ollama API response. --- litellm/llms/ollama/completion/transformation.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/litellm/llms/ollama/completion/transformation.py b/litellm/llms/ollama/completion/transformation.py index ac41310fb61..df38e2c2352 100644 --- a/litellm/llms/ollama/completion/transformation.py +++ b/litellm/llms/ollama/completion/transformation.py @@ -321,6 +321,18 @@ class OllamaConfig(BaseConfig): json_mode: bool | None = None, ) -> ModelResponse: response_json: Final = _OLLAMA_GENERATE_RESPONSE.validate_python(raw_response.json()) + if "error" in response_json: + raise OllamaError( + status_code=raw_response.status_code, + message=f"Ollama returned an error body with status {raw_response.status_code}: {response_json['error']}", + headers=dict(raw_response.headers), + ) + if "response" not in response_json and "message" not in response_json: + raise OllamaError( + status_code=raw_response.status_code, + message=f"Ollama response body is not a completion: {raw_response.text}", + headers=dict(raw_response.headers), + ) ## RESPONSE OBJECT model_response.choices[0].finish_reason = "stop" if request_data.get("format", "") == "json": From cfcef0900c19225a8ba62cce42c07600c1418bb1 Mon Sep 17 00:00:00 2001 From: Lakshya Dhariwal Date: Sun, 18 Oct 2026 13:02:18 +0530 Subject: [PATCH 2/4] fix(fireworks_ai): raise on non-completion 200 response bodies Refactor model_cost iteration to unpack items directly. Add error handling for Fireworks AI response validation. --- litellm/llms/fireworks_ai/chat/transformation.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/litellm/llms/fireworks_ai/chat/transformation.py b/litellm/llms/fireworks_ai/chat/transformation.py index 8b6946ee562..2b3b0535d18 100644 --- a/litellm/llms/fireworks_ai/chat/transformation.py +++ b/litellm/llms/fireworks_ai/chat/transformation.py @@ -544,7 +544,7 @@ class FireworksAIConfig(FireworksAIMixin, OpenAIGPTConfig): return cached[2] index: Final[list[tuple[str, dict]]] = [] - for key, model_info in model_cost.items(): + for key, model_info in (*model_cost.items(),): if not key.startswith("fireworks_ai/"): continue if not isinstance(model_info, dict): @@ -760,6 +760,18 @@ class FireworksAIConfig(FireworksAIMixin, OpenAIGPTConfig): additional_headers: Final = get_response_headers(raw_response_headers) completion_fields: Final = _JSON_DICT.validate_python(completion_response) + if "error" in completion_fields: + raise FireworksAIException( + message=f"Fireworks AI returned an error body with status {raw_response.status_code}: {completion_fields['error']}", + status_code=raw_response.status_code, + headers=getattr(raw_response, "headers", None), + ) + if completion_fields.get("choices") is None: + raise FireworksAIException( + message=f"Fireworks AI response body is not a completion: {raw_response.text}", + status_code=raw_response.status_code, + headers=getattr(raw_response, "headers", None), + ) response: Final = ModelResponse(**completion_fields) if response.model is not None: From 204c82b37f62826e9163202d2a5704078a5b1846 Mon Sep 17 00:00:00 2001 From: Lakshya Dhariwal Date: Sun, 18 Oct 2026 13:02:58 +0530 Subject: [PATCH 3/4] test(ollama): cover non-completion 200 response bodies Add test case for handling malformed response bodies in Ollama transformation. --- .../test_ollama_completion_transformation.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/unit/llms/ollama/test_ollama_completion_transformation.py b/tests/unit/llms/ollama/test_ollama_completion_transformation.py index 248e745ebff..e299057c882 100644 --- a/tests/unit/llms/ollama/test_ollama_completion_transformation.py +++ b/tests/unit/llms/ollama/test_ollama_completion_transformation.py @@ -12,6 +12,7 @@ import respx import litellm from litellm._uuid import uuid from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler +from litellm.llms.ollama.common_utils import OllamaError from litellm.llms.ollama.completion.transformation import ( OllamaConfig, OllamaTextCompletionResponseIterator, @@ -990,3 +991,42 @@ def test_get_complete_url_ignores_a_trailing_slash_on_api_base(api_base: str): ) == "http://gateway.example.com/ollama/api/generate" ) + + +class TestOllamaTransformResponseMalformedBody: + @pytest.mark.parametrize( + "body", + [ + {}, + {"choices": None}, + {"error": {"message": "upstream broke"}}, + ], + ) + def test_transform_response_non_completion_body_raises(self, body): + # A 200 whose body is not a generation response must raise instead of + # returning an empty successful completion. + config = OllamaConfig() + + raw_response = MagicMock() + raw_response.status_code = 200 + raw_response.text = json.dumps(body) + raw_response.headers = {} + raw_response.json.return_value = body + + model_response = ModelResponse( + id="test_id", + choices=[{"message": Message(content="")}], + ) + + with pytest.raises(OllamaError): + config.transform_response( + model="llama2", + raw_response=raw_response, + model_response=model_response, + logging_obj=MagicMock(), + request_data={}, + messages=[], + optional_params={}, + litellm_params={}, + encoding=MagicMock(), + ) From 2ff4574d085645d43e74941305294f04a848a601 Mon Sep 17 00:00:00 2001 From: Lakshya Dhariwal Date: Sun, 18 Oct 2026 13:03:51 +0530 Subject: [PATCH 4/4] test(fireworks_ai): cover non-completion 200 response bodies Add test for handling non-completion response bodies in FireworksAIConfig. --- .../test_fireworks_ai_chat_transformation.py | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/tests/unit/llms/fireworks_ai/chat/test_fireworks_ai_chat_transformation.py b/tests/unit/llms/fireworks_ai/chat/test_fireworks_ai_chat_transformation.py index 04b791cf289..57df2621f52 100644 --- a/tests/unit/llms/fireworks_ai/chat/test_fireworks_ai_chat_transformation.py +++ b/tests/unit/llms/fireworks_ai/chat/test_fireworks_ai_chat_transformation.py @@ -12,7 +12,10 @@ from litellm.constants import SESSION_ID_GENERATED_METADATA_KEY from litellm.litellm_core_utils.get_supported_openai_params import get_supported_openai_params from litellm.llms.custom_httpx.http_handler import HTTPHandler from litellm.llms.fireworks_ai.chat.transformation import FireworksAIConfig -from litellm.llms.fireworks_ai.common_utils import get_fireworks_session_id +from litellm.llms.fireworks_ai.common_utils import ( + FireworksAIException, + get_fireworks_session_id, +) from litellm.types.utils import ( ChatCompletionMessageToolCall, Function, @@ -2358,3 +2361,36 @@ def test_global_disable_flag_with_transform_messages_helper( ) assert json.loads(route.calls.last.request.content)["messages"] == [{"role": "user", "content": content}] + + +@pytest.mark.parametrize( + "body", + [ + {}, + {"choices": None}, + {"error": {"message": "upstream broke"}}, + ], +) +def test_transform_response_non_completion_body_raises(body): + # A 200 whose body is not a chat completion must raise instead of + # returning an empty successful completion. + config = FireworksAIConfig() + + raw_response = MagicMock() + raw_response.status_code = 200 + raw_response.text = json.dumps(body) + raw_response.headers = {} + raw_response.json.return_value = body + + with pytest.raises(FireworksAIException): + config.transform_response( + model="accounts/fireworks/models/llama-v3p1-8b-instruct", + raw_response=raw_response, + model_response=ModelResponse(), + logging_obj=MagicMock(), + request_data={}, + messages=[], + optional_params={}, + litellm_params={}, + encoding=MagicMock(), + )