From f7ea88f3dae1dd5ef75c3f78197d3b73c2947afe Mon Sep 17 00:00:00 2001 From: TemoSulava Date: Thu, 22 Oct 2026 21:50:08 +0400 Subject: [PATCH] fix(exceptions): map OpenAI and Moonshot context window 400s to ContextWindowExceededError OpenAI now words its context_length_exceeded error as "Your input exceeds the context window of this model", and Moonshot returns "Your request exceeded model token limit". Neither matched is_error_str_context_window_exceeded, so both surfaced as a plain BadRequestError and context_window_fallbacks never ran. --- .../exception_mapping_utils.py | 2 + .../test_exception_mapping_utils.py | 38 +++++++++++++++++++ .../responses/test_openai_responses_http.py | 2 +- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/litellm/litellm_core_utils/exception_mapping_utils.py b/litellm/litellm_core_utils/exception_mapping_utils.py index 1c816b22ef1..556da262662 100644 --- a/litellm/litellm_core_utils/exception_mapping_utils.py +++ b/litellm/litellm_core_utils/exception_mapping_utils.py @@ -103,6 +103,8 @@ class ExceptionCheckers: "exceeds the available context size", # llama.cpp/Lemonade "exceeds the maximum number of tokens allowed", # Gemini '"code": "context_length_exceeded"', + "exceeds the context window of this model", # OpenAI + "exceeded model token limit", # Moonshot ] for substring in known_exception_substrings: if substring in _error_str_lowercase: diff --git a/tests/unit/litellm_core_utils/test_exception_mapping_utils.py b/tests/unit/litellm_core_utils/test_exception_mapping_utils.py index 9e8e9cf2a80..f438a8e5408 100644 --- a/tests/unit/litellm_core_utils/test_exception_mapping_utils.py +++ b/tests/unit/litellm_core_utils/test_exception_mapping_utils.py @@ -242,6 +242,13 @@ context_window_test_cases = [ "Input should be 'error' or 'truncate', field: 'context_length_exceeded_behavior', value: 'foo'", False, ), + # OpenAI's current wording (code context_length_exceeded) + ( + "Your input exceeds the context window of this model. Please adjust your input and try again.", + True, + ), + # Moonshot + ("Invalid request: Your request exceeded model token limit: 262144", True), # Negative cases (should return False) ("A generic API error occurred.", False), ("Invalid API Key provided.", False), @@ -439,6 +446,37 @@ def test_gemini_context_window_error_mapping(error_message, should_raise_context ) +@pytest.mark.parametrize( + "custom_llm_provider, model, error_message", + [ + ( + "openai", + "gpt-5", + "Error code: 400 - {'error': {'message': 'Your input exceeds the context window of this model. " + "Please adjust your input and try again.', 'type': 'invalid_request_error', 'param': 'input', " + "'code': 'context_length_exceeded'}}", + ), + ( + "moonshot", + "kimi-k2", + "Error code: 400 - {'error': {'message': 'Invalid request: Your request exceeded model token limit: " + "262144', 'type': 'invalid_request_error', 'param': None, 'code': None}}", + ), + ], +) +def test_openai_compatible_context_window_error_mapping(custom_llm_provider, model, error_message): + """These 400s must map to ContextWindowExceededError so context_window_fallbacks fire.""" + with pytest.raises(litellm.ContextWindowExceededError) as excinfo: + exception_type( + model=model, + original_exception=OpenAIError(status_code=400, message=error_message), + custom_llm_provider=custom_llm_provider, + ) + + assert excinfo.value.status_code == 400 + assert excinfo.value.llm_provider == custom_llm_provider + + def test_lemonade_context_window_error_mapping(): """Lemonade's llama.cpp backend should map context overflows to LiteLLM's standard error.""" diff --git a/tests/unit/llms/openai/responses/test_openai_responses_http.py b/tests/unit/llms/openai/responses/test_openai_responses_http.py index 97067881faf..aedf840f835 100644 --- a/tests/unit/llms/openai/responses/test_openai_responses_http.py +++ b/tests/unit/llms/openai/responses/test_openai_responses_http.py @@ -593,7 +593,7 @@ async def test_responses_stream_error_event_raises_bad_request() -> None: stream: Final = await litellm.aresponses( model="openai/gpt-5-mini", api_key="sk-test", input="oversized prompt", stream=True ) - with pytest.raises(litellm.BadRequestError) as exc_info: + with pytest.raises(litellm.ContextWindowExceededError) as exc_info: _ = [event async for event in stream] requests: Final = tuple(route.calls)