diff --git a/litellm/litellm_core_utils/core_helpers.py b/litellm/litellm_core_utils/core_helpers.py index 40592595a33..2462c282041 100644 --- a/litellm/litellm_core_utils/core_helpers.py +++ b/litellm/litellm_core_utils/core_helpers.py @@ -131,6 +131,9 @@ _FINISH_REASON_MAP: Final[dict[str, OpenAIChatCompletionFinishReason]] = { "content_filter": "content_filter", # Anthropic Sonnet 4 "content_filtered": "content_filter", + # Generic error passthrough (OpenRouter and other OpenAI-compatible providers + # emit lowercase "error" when a provider fails mid-stream) + "error": "stop", } diff --git a/tests/test_litellm/litellm_core_utils/test_core_helpers.py b/tests/test_litellm/litellm_core_utils/test_core_helpers.py index b4f539da286..aa7f2990c13 100644 --- a/tests/test_litellm/litellm_core_utils/test_core_helpers.py +++ b/tests/test_litellm/litellm_core_utils/test_core_helpers.py @@ -192,6 +192,19 @@ class TestMapFinishReasonOpenAIPassthrough: assert map_finish_reason(reason) == reason +class TestMapFinishReasonGenericError: + def test_lowercase_error_is_explicitly_mapped(self): + assert "error" in _FINISH_REASON_MAP + assert map_finish_reason("error") == "stop" + + def test_lowercase_error_does_not_warn(self, mocker): + warn = mocker.patch( + "litellm.litellm_core_utils.core_helpers.verbose_logger.warning" + ) + assert map_finish_reason("error") == "stop" + warn.assert_not_called() + + class TestMapFinishReasonUnknown: def test_unknown_value_defaults_to_stop(self): assert map_finish_reason("some_unknown_value") == "stop"