From 20f26abefb7331674a16584c7e4e8d18786bda07 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Thu, 29 Oct 2026 02:03:18 -0700 Subject: [PATCH 1/5] fix(cost-map): raise four bare Bedrock Converse rows to their 262144 input window moonshotai.kimi-k2.5, qwen.qwen3-coder-next, qwen.qwen3-next-80b-a3b and nvidia.nemotron-nano-3-30b said 256000 while their bedrock/ and regional rows and Bedrock itself allow 262144, so router pre-call checks refused prompts Bedrock accepts. Adds a test that a bare Bedrock row is never narrower than its bedrock/ rows. --- ...odel_prices_and_context_window_backup.json | 8 ++-- model_prices_and_context_window.json | 8 ++-- .../test_model_prices_and_context_window.py | 44 +++++++++++++++++++ 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 23c6ccf9c0a..4d2bafb0680 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -43028,7 +43028,7 @@ "input_cost_per_token_flex": 3e-07, "input_cost_per_token_priority": 1.05e-06, "litellm_provider": "bedrock_converse", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 16000, "mode": "chat", "output_cost_per_token": 3e-06, @@ -44262,7 +44262,7 @@ "input_cost_per_token_flex": 3e-08, "input_cost_per_token_priority": 1.1e-07, "litellm_provider": "bedrock_converse", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.4e-07, @@ -46675,7 +46675,7 @@ "qwen.qwen3-next-80b-a3b": { "input_cost_per_token": 1.5e-07, "litellm_provider": "bedrock_converse", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.2e-06, @@ -46802,7 +46802,7 @@ "input_cost_per_token_flex": 2.5e-07, "input_cost_per_token_priority": 8.75e-07, "litellm_provider": "bedrock_converse", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 16000, "mode": "chat", "output_cost_per_token": 1.2e-06, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 23c6ccf9c0a..4d2bafb0680 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -43028,7 +43028,7 @@ "input_cost_per_token_flex": 3e-07, "input_cost_per_token_priority": 1.05e-06, "litellm_provider": "bedrock_converse", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 16000, "mode": "chat", "output_cost_per_token": 3e-06, @@ -44262,7 +44262,7 @@ "input_cost_per_token_flex": 3e-08, "input_cost_per_token_priority": 1.1e-07, "litellm_provider": "bedrock_converse", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 8000, "mode": "chat", "output_cost_per_token": 2.4e-07, @@ -46675,7 +46675,7 @@ "qwen.qwen3-next-80b-a3b": { "input_cost_per_token": 1.5e-07, "litellm_provider": "bedrock_converse", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 8000, "mode": "chat", "output_cost_per_token": 1.2e-06, @@ -46802,7 +46802,7 @@ "input_cost_per_token_flex": 2.5e-07, "input_cost_per_token_priority": 8.75e-07, "litellm_provider": "bedrock_converse", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 16000, "mode": "chat", "output_cost_per_token": 1.2e-06, diff --git a/tests/unit/test_model_prices_and_context_window.py b/tests/unit/test_model_prices_and_context_window.py index 2aceed493a7..544ce00216e 100644 --- a/tests/unit/test_model_prices_and_context_window.py +++ b/tests/unit/test_model_prices_and_context_window.py @@ -3,6 +3,8 @@ from __future__ import annotations import json import re from collections.abc import Iterator, Mapping +from itertools import groupby +from operator import itemgetter from pathlib import Path from types import MappingProxyType from typing import Final @@ -924,3 +926,45 @@ def test_bedrock_gpt_oss_rows_declare_vision_support_like_their_safeguard_siblin def test_bedrock_gpt_oss_vision_is_explicitly_disabled_for_the_router_gate() -> None: assert litellm.utils.is_vision_explicitly_disabled("bedrock/openai.gpt-oss-120b-1:0") is True assert litellm.utils.is_vision_explicitly_disabled("bedrock_mantle/openai.gpt-oss-20b") is True + + +class _BedrockWindowRow(BaseModel): + model_config = ConfigDict(frozen=True, extra="ignore") + + litellm_provider: str + max_input_tokens: int | None = None + + +_BEDROCK_PROVIDERS: Final = frozenset({"bedrock", "bedrock_converse"}) +_BEDROCK_PREFIXED_ROW: Final = re.compile(r"bedrock/(?:[a-z0-9-]+/)?(?P[^/]+)") + + +def _bedrock_input_windows(path: Path) -> Mapping[str, int]: + prices: Final = TypeAdapter(dict[str, JsonValue]).validate_json(path.read_bytes()) + rows: Final = tuple( + (key, _BedrockWindowRow.model_validate(row)) + for key, row in prices.items() + if isinstance(row, dict) and row.get("litellm_provider") in _BEDROCK_PROVIDERS + ) + return MappingProxyType({key: row.max_input_tokens for key, row in rows if row.max_input_tokens is not None}) + + +def _widest_prefixed_window_per_model(windows: Mapping[str, int]) -> Mapping[str, int]: + prefixed: Final = sorted( + (match["model"], window) for key, window in windows.items() if (match := _BEDROCK_PREFIXED_ROW.fullmatch(key)) + ) + return MappingProxyType( + {model: max(window for _, window in group) for model, group in groupby(prefixed, key=itemgetter(0))} + ) + + +@pytest.mark.parametrize("path", (PRICES_PATH, BACKUP_PRICES_PATH), ids=("main", "backup")) +def test_bare_bedrock_rows_admit_every_prompt_their_prefixed_rows_admit(path: Path) -> None: + windows: Final = _bedrock_input_windows(path) + widest: Final = _widest_prefixed_window_per_model(windows) + bare_with_prefixed_rows: Final = tuple(key for key in windows if "/" not in key and key in widest) + assert bare_with_prefixed_rows, f"No bare Bedrock row with a bedrock/ counterpart in {path}" + narrower: Final = tuple( + f"{key}: {windows[key]} < {widest[key]}" for key in bare_with_prefixed_rows if windows[key] < widest[key] + ) + assert narrower == () From 5fc4f7f66a0431e67a1253185e15426e0f31a54f Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 2 Nov 2026 19:26:22 -0800 Subject: [PATCH 2/5] fix(bedrock): map Bedrock's maximum context length 400 to ContextWindowExceededError and raise three same-model rows to 262144 --- .../exception_mapping_utils.py | 1 + ...odel_prices_and_context_window_backup.json | 6 ++--- model_prices_and_context_window.json | 6 ++--- .../test_exception_mapping_utils.py | 23 +++++++++++++++++++ .../test_model_prices_and_context_window.py | 17 +++++++++++++- 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/litellm/litellm_core_utils/exception_mapping_utils.py b/litellm/litellm_core_utils/exception_mapping_utils.py index 47ec8331c83..4d2fbc9cef8 100644 --- a/litellm/litellm_core_utils/exception_mapping_utils.py +++ b/litellm/litellm_core_utils/exception_mapping_utils.py @@ -952,6 +952,7 @@ def _map_bedrock_exception( or "prompt is too long" in error_str or "prompt: length: 1.." in error_str or "Too many input tokens" in error_str + or ExceptionCheckers.is_error_str_context_window_exceeded(error_str) ): raise ContextWindowExceededError( message=f"BedrockException: Context Window Error - {error_str}", diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 4d2bafb0680..1359f5fa43f 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -43007,7 +43007,7 @@ "input_cost_per_token_batches": 3e-07, "input_cost_per_token_priority": 1.05e-06, "litellm_provider": "bedrock_converse", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 16000, "mode": "chat", "output_cost_per_token": 2.5e-06, @@ -68376,7 +68376,7 @@ "output_cost_per_token": 2.5e-06, "output_cost_per_token_flex": 1.25e-06, "litellm_provider": "bedrock_mantle", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 16000, "mode": "chat", "output_cost_per_token_batches": 1.25e-06, @@ -68481,7 +68481,7 @@ "output_cost_per_token": 1.2e-06, "output_cost_per_token_flex": 6e-07, "litellm_provider": "bedrock_mantle", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 6e-07, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 4d2bafb0680..1359f5fa43f 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -43007,7 +43007,7 @@ "input_cost_per_token_batches": 3e-07, "input_cost_per_token_priority": 1.05e-06, "litellm_provider": "bedrock_converse", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 16000, "mode": "chat", "output_cost_per_token": 2.5e-06, @@ -68376,7 +68376,7 @@ "output_cost_per_token": 2.5e-06, "output_cost_per_token_flex": 1.25e-06, "litellm_provider": "bedrock_mantle", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 16000, "mode": "chat", "output_cost_per_token_batches": 1.25e-06, @@ -68481,7 +68481,7 @@ "output_cost_per_token": 1.2e-06, "output_cost_per_token_flex": 6e-07, "litellm_provider": "bedrock_mantle", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 6e-07, 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 55e7c47e2c9..313c4a94976 100644 --- a/tests/unit/litellm_core_utils/test_exception_mapping_utils.py +++ b/tests/unit/litellm_core_utils/test_exception_mapping_utils.py @@ -1357,6 +1357,7 @@ PROVIDERS_THAT_RECOGNISE_A_FULL_CONTEXT_WINDOW = ( "anthropic", "azure", "azure_ai", + "bedrock", "databricks", "deepseek", "fireworks_ai", @@ -1728,6 +1729,28 @@ def test_bedrock_500_preserves_provider_response_headers(): assert exc_info.value.response.headers["x-amzn-requestid"] == "req-map-500" +def test_bedrock_converse_maximum_context_length_error_maps_to_context_window_exceeded(): + original_exception: Final = BedrockError( + status_code=400, + message=( + '{"message":"The model returned the following errors: ErrorEvent { error: APIError { type: ' + '\\"BadRequestError\\", code: Some(400), message: \\"This model\'s maximum context length is 262144 ' + "tokens. However, you requested 16 output tokens and your prompt contains at least 262129 input tokens" + '\\" } }"}' + ), + ) + + with pytest.raises(litellm.ContextWindowExceededError) as exc_info: + exception_type( + model="qwen.qwen3-coder-next", + original_exception=original_exception, + custom_llm_provider="bedrock", + ) + + assert exc_info.value.status_code == 400 + assert "maximum context length is 262144 tokens" in exc_info.value.message + + @pytest.mark.parametrize( "custom_llm_provider, status_code, provider_message, expected_exception", [ diff --git a/tests/unit/test_model_prices_and_context_window.py b/tests/unit/test_model_prices_and_context_window.py index 544ce00216e..87bc031a790 100644 --- a/tests/unit/test_model_prices_and_context_window.py +++ b/tests/unit/test_model_prices_and_context_window.py @@ -935,7 +935,12 @@ class _BedrockWindowRow(BaseModel): max_input_tokens: int | None = None -_BEDROCK_PROVIDERS: Final = frozenset({"bedrock", "bedrock_converse"}) +_BEDROCK_PROVIDERS: Final = frozenset({"bedrock", "bedrock_converse", "bedrock_mantle"}) +_ROWS_NAMING_A_PREFIXED_MODEL_UNDER_ANOTHER_ID: Final = ( + ("moonshot.kimi-k2-thinking", "moonshotai.kimi-k2-thinking"), + ("bedrock_mantle/moonshotai.kimi-k2-thinking", "moonshotai.kimi-k2-thinking"), + ("bedrock_mantle/qwen.qwen3-next-80b-a3b-instruct", "qwen.qwen3-next-80b-a3b"), +) _BEDROCK_PREFIXED_ROW: Final = re.compile(r"bedrock/(?:[a-z0-9-]+/)?(?P[^/]+)") @@ -968,3 +973,13 @@ def test_bare_bedrock_rows_admit_every_prompt_their_prefixed_rows_admit(path: Pa f"{key}: {windows[key]} < {widest[key]}" for key in bare_with_prefixed_rows if windows[key] < widest[key] ) assert narrower == () + + +@pytest.mark.parametrize("path", (PRICES_PATH, BACKUP_PRICES_PATH), ids=("main", "backup")) +@pytest.mark.parametrize(("row", "model"), _ROWS_NAMING_A_PREFIXED_MODEL_UNDER_ANOTHER_ID) +def test_bedrock_rows_under_another_model_id_admit_every_prompt_its_prefixed_rows_admit( + path: Path, row: str, model: str +) -> None: + windows: Final = _bedrock_input_windows(path) + widest: Final = _widest_prefixed_window_per_model(windows) + assert windows[row] >= widest[model] From 6777e82f422b85fc2091323a3fa381c4870e128e Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 2 Nov 2026 21:01:55 -0800 Subject: [PATCH 3/5] fix(bedrock): widen three Mantle Qwen rows and say prompt is too long on a served model's Mantle overflow Mantle's Qwen3 235B, Qwen3 Coder 30B and Qwen3 32B rows were narrower than their Converse rows, and Mantle took the longer prompts live. A Mantle overflow carrying the served model's own text now gets the same prompt is too long message as Mantle's other overflow shapes, so Claude Code compacts --- .../exception_mapping_utils.py | 12 +++++---- ...odel_prices_and_context_window_backup.json | 6 ++--- model_prices_and_context_window.json | 6 ++--- .../test_exception_mapping_utils.py | 25 +++++++++++++++++++ .../test_model_prices_and_context_window.py | 20 +++++++++++---- 5 files changed, 53 insertions(+), 16 deletions(-) diff --git a/litellm/litellm_core_utils/exception_mapping_utils.py b/litellm/litellm_core_utils/exception_mapping_utils.py index 4d2fbc9cef8..9da6cb6afea 100644 --- a/litellm/litellm_core_utils/exception_mapping_utils.py +++ b/litellm/litellm_core_utils/exception_mapping_utils.py @@ -912,16 +912,18 @@ def _bedrock_mantle_context_window_response(original_exception: _ProviderHTTPExc def _get_bedrock_mantle_context_window_message(error_str: str) -> str | None: """ - Mantle reports context overflow as a validation_error carrying the token counts, or as - OpenAI's context_length_exceeded code, either in a 400 body or in a streamed error event - with no error type. Clients such as Claude Code only treat "prompt is too long" as - overflow (litellm/litellm#36546). + Mantle reports context overflow as a validation_error carrying the token counts, as + OpenAI's context_length_exceeded code, or as the served model's own overflow text, either + in a 400 body or in a streamed error event with no error type. Clients such as Claude Code + only treat "prompt is too long" as overflow (litellm/litellm#36546). """ match = _BEDROCK_MANTLE_CONTEXT_WINDOW_PATTERN.search(error_str) if match is not None: prompt_tokens, max_tokens = match.groups() return f"prompt is too long: {prompt_tokens} tokens > {max_tokens} maximum" - if _BEDROCK_MANTLE_CONTEXT_LENGTH_EXCEEDED_CODE_PATTERN.search(error_str) is not None: + if _BEDROCK_MANTLE_CONTEXT_LENGTH_EXCEEDED_CODE_PATTERN.search( + error_str + ) is not None or ExceptionCheckers.is_error_str_context_window_exceeded(error_str): return _BEDROCK_MANTLE_CONTEXT_WINDOW_GENERIC_MESSAGE return None diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 1359f5fa43f..69c82e1c1a1 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -68397,7 +68397,7 @@ "output_cost_per_token": 8.8e-07, "output_cost_per_token_flex": 4.4e-07, "litellm_provider": "bedrock_mantle", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 4.4e-07, @@ -68418,7 +68418,7 @@ "output_cost_per_token": 6e-07, "output_cost_per_token_flex": 3e-07, "litellm_provider": "bedrock_mantle", - "max_input_tokens": 32000, + "max_input_tokens": 32768, "max_output_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 3e-07, @@ -68440,7 +68440,7 @@ "output_cost_per_token": 6e-07, "output_cost_per_token_flex": 3e-07, "litellm_provider": "bedrock_mantle", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 16000, "mode": "chat", "output_cost_per_token_batches": 3e-07, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 1359f5fa43f..69c82e1c1a1 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -68397,7 +68397,7 @@ "output_cost_per_token": 8.8e-07, "output_cost_per_token_flex": 4.4e-07, "litellm_provider": "bedrock_mantle", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 4.4e-07, @@ -68418,7 +68418,7 @@ "output_cost_per_token": 6e-07, "output_cost_per_token_flex": 3e-07, "litellm_provider": "bedrock_mantle", - "max_input_tokens": 32000, + "max_input_tokens": 32768, "max_output_tokens": 8000, "mode": "chat", "output_cost_per_token_batches": 3e-07, @@ -68440,7 +68440,7 @@ "output_cost_per_token": 6e-07, "output_cost_per_token_flex": 3e-07, "litellm_provider": "bedrock_mantle", - "max_input_tokens": 256000, + "max_input_tokens": 262144, "max_output_tokens": 16000, "mode": "chat", "output_cost_per_token_batches": 3e-07, 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 313c4a94976..fb43fa7cbcd 100644 --- a/tests/unit/litellm_core_utils/test_exception_mapping_utils.py +++ b/tests/unit/litellm_core_utils/test_exception_mapping_utils.py @@ -1516,6 +1516,31 @@ def test_bedrock_mantle_openai_envelope_context_overflow_maps_to_context_window_ assert "prompt is too long" in excinfo.value.message +def test_bedrock_mantle_served_model_overflow_text_maps_to_prompt_is_too_long(): + from litellm.llms.base_llm.chat.transformation import BaseLLMException + + original_exception: Final = BaseLLMException( + status_code=400, + message=( + '{"error":{"code":"validation_error","message":"ErrorEvent { error: APIError { type: ' + '\\"BadRequestError\\", code: Some(400), message: \\"This model\'s maximum context length is 262144 ' + "tokens. However, you requested 16 output tokens and your prompt contains at least 262129 input tokens" + '\\", param: None } }","param":null,"type":"invalid_request_error"}}' + ), + ) + + with pytest.raises(litellm.ContextWindowExceededError) as excinfo: + exception_type( + model="qwen.qwen3-next-80b-a3b-instruct", + original_exception=original_exception, + custom_llm_provider="bedrock_mantle", + ) + + assert excinfo.value.status_code == 400 + assert excinfo.value.llm_provider == "bedrock_mantle" + assert "prompt is too long" in excinfo.value.message + + def test_bedrock_mantle_streamed_context_overflow_event_maps_to_context_window_exceeded(): from litellm.responses.streaming_iterator import map_stream_error_to_exception diff --git a/tests/unit/test_model_prices_and_context_window.py b/tests/unit/test_model_prices_and_context_window.py index 87bc031a790..abc7c1270db 100644 --- a/tests/unit/test_model_prices_and_context_window.py +++ b/tests/unit/test_model_prices_and_context_window.py @@ -936,10 +936,13 @@ class _BedrockWindowRow(BaseModel): _BEDROCK_PROVIDERS: Final = frozenset({"bedrock", "bedrock_converse", "bedrock_mantle"}) -_ROWS_NAMING_A_PREFIXED_MODEL_UNDER_ANOTHER_ID: Final = ( +_ROWS_NAMING_A_MODEL_UNDER_ANOTHER_ID: Final = ( ("moonshot.kimi-k2-thinking", "moonshotai.kimi-k2-thinking"), ("bedrock_mantle/moonshotai.kimi-k2-thinking", "moonshotai.kimi-k2-thinking"), ("bedrock_mantle/qwen.qwen3-next-80b-a3b-instruct", "qwen.qwen3-next-80b-a3b"), + ("bedrock_mantle/qwen.qwen3-235b-a22b-2507", "qwen.qwen3-235b-a22b-2507-v1:0"), + ("bedrock_mantle/qwen.qwen3-coder-30b-a3b-instruct", "qwen.qwen3-coder-30b-a3b-v1:0"), + ("bedrock_mantle/qwen.qwen3-32b", "qwen.qwen3-32b-v1:0"), ) _BEDROCK_PREFIXED_ROW: Final = re.compile(r"bedrock/(?:[a-z0-9-]+/)?(?P[^/]+)") @@ -975,11 +978,18 @@ def test_bare_bedrock_rows_admit_every_prompt_their_prefixed_rows_admit(path: Pa assert narrower == () +def _widest_window_of_model(windows: Mapping[str, int], model: str) -> int: + return max( + window + for key, window in windows.items() + if key == model or ((match := _BEDROCK_PREFIXED_ROW.fullmatch(key)) is not None and match["model"] == model) + ) + + @pytest.mark.parametrize("path", (PRICES_PATH, BACKUP_PRICES_PATH), ids=("main", "backup")) -@pytest.mark.parametrize(("row", "model"), _ROWS_NAMING_A_PREFIXED_MODEL_UNDER_ANOTHER_ID) -def test_bedrock_rows_under_another_model_id_admit_every_prompt_its_prefixed_rows_admit( +@pytest.mark.parametrize(("row", "model"), _ROWS_NAMING_A_MODEL_UNDER_ANOTHER_ID) +def test_bedrock_rows_under_another_model_id_admit_every_prompt_that_model_admits( path: Path, row: str, model: str ) -> None: windows: Final = _bedrock_input_windows(path) - widest: Final = _widest_prefixed_window_per_model(windows) - assert windows[row] >= widest[model] + assert windows[row] >= _widest_window_of_model(windows, model) From 2e38841c7a211e0c1e077ff65a1059f8dd4ec5ad Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 2 Nov 2026 22:10:27 -0800 Subject: [PATCH 4/5] fix(bedrock): say prompt is too long on every Bedrock overflow Bedrock and Mantle overflow errors that map to ContextWindowExceededError now carry "prompt is too long" once, so Claude Code reads them as an overflow and compacts instead of showing a raw API error --- .../exception_mapping_utils.py | 14 ++++++++++---- .../test_exception_mapping_utils.py | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/litellm/litellm_core_utils/exception_mapping_utils.py b/litellm/litellm_core_utils/exception_mapping_utils.py index 9da6cb6afea..a58c0670bde 100644 --- a/litellm/litellm_core_utils/exception_mapping_utils.py +++ b/litellm/litellm_core_utils/exception_mapping_utils.py @@ -921,13 +921,19 @@ def _get_bedrock_mantle_context_window_message(error_str: str) -> str | None: if match is not None: prompt_tokens, max_tokens = match.groups() return f"prompt is too long: {prompt_tokens} tokens > {max_tokens} maximum" - if _BEDROCK_MANTLE_CONTEXT_LENGTH_EXCEEDED_CODE_PATTERN.search( - error_str - ) is not None or ExceptionCheckers.is_error_str_context_window_exceeded(error_str): + if _BEDROCK_MANTLE_CONTEXT_LENGTH_EXCEEDED_CODE_PATTERN.search(error_str) is not None: + return _BEDROCK_MANTLE_CONTEXT_WINDOW_GENERIC_MESSAGE + if ExceptionCheckers.is_error_str_context_window_exceeded(error_str): return _BEDROCK_MANTLE_CONTEXT_WINDOW_GENERIC_MESSAGE return None +def _bedrock_context_window_message(error_str: str) -> str: + if "prompt is too long" in error_str: + return f"BedrockException: Context Window Error - {error_str}" + return f"BedrockException: Context Window Error - prompt is too long: {error_str}" + + def _map_bedrock_exception( *, model: str, @@ -957,7 +963,7 @@ def _map_bedrock_exception( or ExceptionCheckers.is_error_str_context_window_exceeded(error_str) ): raise ContextWindowExceededError( - message=f"BedrockException: Context Window Error - {error_str}", + message=_bedrock_context_window_message(error_str), model=model, llm_provider="bedrock", response=getattr(original_exception, "response", None), 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 fb43fa7cbcd..89773098d12 100644 --- a/tests/unit/litellm_core_utils/test_exception_mapping_utils.py +++ b/tests/unit/litellm_core_utils/test_exception_mapping_utils.py @@ -1774,6 +1774,24 @@ def test_bedrock_converse_maximum_context_length_error_maps_to_context_window_ex assert exc_info.value.status_code == 400 assert "maximum context length is 262144 tokens" in exc_info.value.message + assert "prompt is too long" in exc_info.value.message + + +def test_bedrock_overflow_that_already_says_prompt_is_too_long_is_not_repeated(): + original_exception: Final = BedrockError( + status_code=400, + message='{"message":"prompt is too long: 210000 tokens > 200000 maximum"}', + ) + + with pytest.raises(litellm.ContextWindowExceededError) as exc_info: + exception_type( + model="anthropic.claude-sonnet-4-5-20250929-v1:0", + original_exception=original_exception, + custom_llm_provider="bedrock", + ) + + assert exc_info.value.message.count("prompt is too long") == 1 + assert "210000 tokens > 200000 maximum" in exc_info.value.message @pytest.mark.parametrize( From 71943a78fdd27a8147f3c91349f70b7e7114b5ef Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Mon, 2 Nov 2026 23:36:53 -0800 Subject: [PATCH 5/5] fix(bedrock): keep the Mantle model's overflow text after "prompt is too long" --- litellm/litellm_core_utils/exception_mapping_utils.py | 10 +++++----- .../litellm_core_utils/test_exception_mapping_utils.py | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/litellm/litellm_core_utils/exception_mapping_utils.py b/litellm/litellm_core_utils/exception_mapping_utils.py index a58c0670bde..22480cb179d 100644 --- a/litellm/litellm_core_utils/exception_mapping_utils.py +++ b/litellm/litellm_core_utils/exception_mapping_utils.py @@ -924,14 +924,14 @@ def _get_bedrock_mantle_context_window_message(error_str: str) -> str | None: if _BEDROCK_MANTLE_CONTEXT_LENGTH_EXCEEDED_CODE_PATTERN.search(error_str) is not None: return _BEDROCK_MANTLE_CONTEXT_WINDOW_GENERIC_MESSAGE if ExceptionCheckers.is_error_str_context_window_exceeded(error_str): - return _BEDROCK_MANTLE_CONTEXT_WINDOW_GENERIC_MESSAGE + return _with_prompt_is_too_long(error_str) return None -def _bedrock_context_window_message(error_str: str) -> str: +def _with_prompt_is_too_long(error_str: str) -> str: if "prompt is too long" in error_str: - return f"BedrockException: Context Window Error - {error_str}" - return f"BedrockException: Context Window Error - prompt is too long: {error_str}" + return error_str + return f"prompt is too long: {error_str}" def _map_bedrock_exception( @@ -963,7 +963,7 @@ def _map_bedrock_exception( or ExceptionCheckers.is_error_str_context_window_exceeded(error_str) ): raise ContextWindowExceededError( - message=_bedrock_context_window_message(error_str), + message=f"BedrockException: Context Window Error - {_with_prompt_is_too_long(error_str)}", model=model, llm_provider="bedrock", response=getattr(original_exception, "response", None), 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 89773098d12..4afa86b4bf4 100644 --- a/tests/unit/litellm_core_utils/test_exception_mapping_utils.py +++ b/tests/unit/litellm_core_utils/test_exception_mapping_utils.py @@ -1539,6 +1539,7 @@ def test_bedrock_mantle_served_model_overflow_text_maps_to_prompt_is_too_long(): assert excinfo.value.status_code == 400 assert excinfo.value.llm_provider == "bedrock_mantle" assert "prompt is too long" in excinfo.value.message + assert "your prompt contains at least 262129 input tokens" in excinfo.value.message def test_bedrock_mantle_streamed_context_overflow_event_maps_to_context_window_exceeded():