diff --git a/litellm/anthropic_beta_headers_config.json b/litellm/anthropic_beta_headers_config.json
index bdfddb48522..95d1db5c684 100644
--- a/litellm/anthropic_beta_headers_config.json
+++ b/litellm/anthropic_beta_headers_config.json
@@ -200,6 +200,7 @@
"bash_20250124": null,
"code-execution-2025-08-25": null,
"compact-2026-01-12": "compact-2026-01-12",
+ "compact-2026-09-04": "compact-2026-09-04",
"computer-use-2025-01-24": "computer-use-2025-01-24",
"computer-use-2025-11-24": "computer-use-2025-11-24",
"context-1m-2025-08-07": "context-1m-2025-08-07",
diff --git a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude_transformation.py b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude_transformation.py
index cf48fcf5c2f..c2437e67699 100644
--- a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude_transformation.py
+++ b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude_transformation.py
@@ -23,6 +23,7 @@ from litellm.llms.anthropic.common_utils import (
OBJECT_MAPPING_ADAPTER,
AnthropicModelInfo,
carried_thinking_keys,
+ carries_signed_compaction_block,
validated_object_mapping,
)
from litellm.llms.anthropic.messages.native.transformation import (
@@ -571,6 +572,22 @@ class AmazonAnthropicClaudeMessagesConfig(
"edits": retained_edits,
}
+ @staticmethod
+ def _add_compaction_beta_for_bedrock_invoke(anthropic_messages_request: dict, beta_set: set) -> None:
+ """
+ A top-level ``compaction`` request needs the ``compact-2026-09-04``
+ anthropic-beta; without it Bedrock answers 400 "compaction: Extra inputs
+ are not permitted". A later request that adopts the returned signed
+ block needs the same beta, so it is added for that block too, as the
+ Anthropic route does.
+
+ Ref: https://docs.aws.amazon.com/bedrock/latest/userguide/claude-messages-compaction.html
+ """
+ if isinstance(anthropic_messages_request.get("compaction"), dict) or carries_signed_compaction_block(
+ anthropic_messages_request.get("messages") or ()
+ ):
+ beta_set.add("compact-2026-09-04")
+
def strips_beta_gated_fields_from_final_headers(self) -> bool:
return False
@@ -639,6 +656,7 @@ class AmazonAnthropicClaudeMessagesConfig(
anthropic_messages_request=anthropic_messages_request,
beta_set=beta_set,
)
+ self._add_compaction_beta_for_bedrock_invoke(anthropic_messages_request, beta_set)
self._get_tool_search_beta_header_for_bedrock(
model=model,
@@ -858,6 +876,16 @@ class AmazonAnthropicClaudeMessagesConfig(
if filtered_betas:
anthropic_messages_request["anthropic_beta"] = filtered_betas
+ # ``compaction`` travels only with its beta. A beta map without compact-2026-09-04
+ # (bedrock_mantle) or a non-object value keeps the earlier strip instead of a 400.
+ if "compaction" in anthropic_messages_request and not (
+ isinstance(anthropic_messages_request["compaction"], dict) and "compact-2026-09-04" in filtered_betas
+ ):
+ verbose_logger.debug(
+ "Bedrock Invoke: dropping `compaction`; compact-2026-09-04 is not served on this route"
+ )
+ anthropic_messages_request.pop("compaction")
+
final_request: Final = OBJECT_MAPPING_ADAPTER.validate_python(anthropic_messages_request) # pyright: ignore[reportUnknownArgumentType] # built from the base signature's bare dict
gated: Final = strip_beta_gated_native_request(
{},
diff --git a/litellm/types/llms/bedrock.py b/litellm/types/llms/bedrock.py
index ff4f2c4fd1e..f71feeb364b 100644
--- a/litellm/types/llms/bedrock.py
+++ b/litellm/types/llms/bedrock.py
@@ -1257,6 +1257,13 @@ class BedrockInvokeAnthropicMessagesRequest(TypedDict, total=False):
# other edit types (e.g. `clear_thinking_20251015`) never reach Bedrock.
context_management: dict
+ # `compaction` ({"type": "summarize", "instructions"?: str}) is the
+ # `compact-2026-09-04` beta: one summarization call that returns a single
+ # signed compaction block. Bedrock rejects it together with
+ # `context_management`. Ref:
+ # https://docs.aws.amazon.com/bedrock/latest/userguide/claude-messages-compaction.html
+ compaction: dict
+
class BedrockBatchRecordKind(Enum):
"""
diff --git a/tests/unit/llms/bedrock/messages/invoke_transformations/test_anthropic_claude_transformation.py b/tests/unit/llms/bedrock/messages/invoke_transformations/test_anthropic_claude_transformation.py
index c4614dd636f..9c22fd7976f 100644
--- a/tests/unit/llms/bedrock/messages/invoke_transformations/test_anthropic_claude_transformation.py
+++ b/tests/unit/llms/bedrock/messages/invoke_transformations/test_anthropic_claude_transformation.py
@@ -1547,6 +1547,112 @@ def test_bedrock_messages_preserves_compact_context_management_and_adds_beta():
assert result["max_tokens"] == 4096
+@pytest.mark.usefixtures("local_beta_headers_config")
+def test_bedrock_messages_keeps_compaction_param_and_adds_beta():
+ """
+ The ``compact-2026-09-04`` beta uses a top-level ``compaction`` parameter, not a
+ ``context_management`` edit. Bedrock InvokeModel accepts it with that beta and
+ returns one signed compaction block. Before this fix the top-level allowlist
+ dropped ``compaction``, so the request reached Bedrock as an ordinary turn.
+
+ Ref: https://docs.aws.amazon.com/bedrock/latest/userguide/claude-messages-compaction.html
+ """
+ from litellm.types.router import GenericLiteLLMParams
+
+ cfg = AmazonAnthropicClaudeMessagesConfig()
+ messages = [
+ {"role": "user", "content": [{"type": "text", "text": "Is CHG-TR-0042 Type IB in TR-TITCK?"}]},
+ {"role": "assistant", "content": [{"type": "text", "text": "Type IB."}]},
+ {"role": "user", "content": [{"type": "text", "text": "Now KR-MFDS."}]},
+ ]
+ optional_params = {"max_tokens": 1024, "compaction": {"type": "summarize", "instructions": "Keep rule ids."}}
+
+ result = cfg.transform_anthropic_messages_request(
+ model="anthropic.claude-opus-5",
+ messages=messages,
+ anthropic_messages_optional_request_params=optional_params,
+ litellm_params=GenericLiteLLMParams(),
+ headers={},
+ )
+
+ assert result["compaction"] == {"type": "summarize", "instructions": "Keep rule ids."}
+ assert "compact-2026-09-04" in result["anthropic_beta"]
+ assert "context_management" not in result
+ assert set(result).issubset(cfg.BEDROCK_INVOKE_ALLOWED_TOP_LEVEL_FIELDS)
+
+
+@pytest.mark.usefixtures("local_beta_headers_config")
+def test_bedrock_messages_keeps_compact_2026_09_04_beta_for_adopting_request():
+ """
+ A request that adopts the signed block sends it first in ``messages`` and must
+ carry the ``compact-2026-09-04`` beta again. The bedrock beta map has to keep it.
+ """
+ from litellm.types.router import GenericLiteLLMParams
+
+ cfg = AmazonAnthropicClaudeMessagesConfig()
+ block = {"type": "compaction", "content": "TR-TITCK: Type IB.", "signature": "sig"}
+ messages = [{"role": "user", "content": [block, {"type": "text", "text": "Now KR-MFDS."}]}]
+
+ result = cfg.transform_anthropic_messages_request(
+ model="anthropic.claude-opus-5",
+ messages=messages,
+ anthropic_messages_optional_request_params={"max_tokens": 512},
+ litellm_params=GenericLiteLLMParams(),
+ headers={"anthropic-beta": "compact-2026-09-04"},
+ )
+
+ assert "compact-2026-09-04" in result["anthropic_beta"]
+ assert result["messages"][0]["content"][0] == block
+
+
+@pytest.mark.usefixtures("local_beta_headers_config")
+def test_bedrock_messages_adds_compact_2026_09_04_beta_for_signed_block_without_client_header():
+ """
+ A client that adopts the signed block but sends no anthropic-beta header still
+ needs ``compact-2026-09-04``; the Anthropic route adds it for the block, and so
+ does Bedrock Invoke.
+ """
+ from litellm.types.router import GenericLiteLLMParams
+
+ cfg = AmazonAnthropicClaudeMessagesConfig()
+ block = {"type": "compaction", "content": "TR-TITCK: Type IB.", "signature": "sig"}
+ messages = [{"role": "user", "content": [block, {"type": "text", "text": "Now KR-MFDS."}]}]
+
+ result = cfg.transform_anthropic_messages_request(
+ model="anthropic.claude-opus-5",
+ messages=messages,
+ anthropic_messages_optional_request_params={"max_tokens": 512},
+ litellm_params=GenericLiteLLMParams(),
+ headers={},
+ )
+
+ assert result["anthropic_beta"] == ["compact-2026-09-04"]
+ assert result["messages"][0]["content"][0] == block
+
+
+@pytest.mark.usefixtures("local_beta_headers_config")
+@pytest.mark.parametrize("compaction", [None, "summarize", []])
+def test_bedrock_messages_drops_compaction_that_is_not_an_object(compaction):
+ """
+ Only an object ``compaction`` gets the beta. Any other value is dropped, as it
+ was before the field was allowed, instead of reaching Bedrock without its beta.
+ """
+ from litellm.types.router import GenericLiteLLMParams
+
+ cfg = AmazonAnthropicClaudeMessagesConfig()
+
+ result = cfg.transform_anthropic_messages_request(
+ model="anthropic.claude-opus-5",
+ messages=[{"role": "user", "content": [{"type": "text", "text": "Hi"}]}],
+ anthropic_messages_optional_request_params={"max_tokens": 512, "compaction": compaction},
+ litellm_params=GenericLiteLLMParams(),
+ headers={},
+ )
+
+ assert "compaction" not in result
+ assert "compact-2026-09-04" not in result.get("anthropic_beta", [])
+
+
def test_bedrock_messages_filters_unsupported_context_management_edits():
"""
Mixed edit lists must drop the LiteLLM-internal ``clear_thinking_20251015``
diff --git a/tests/unit/llms/bedrock/test_mantle.py b/tests/unit/llms/bedrock/test_mantle.py
index 63af0105f5b..d6a802366c7 100644
--- a/tests/unit/llms/bedrock/test_mantle.py
+++ b/tests/unit/llms/bedrock/test_mantle.py
@@ -796,3 +796,26 @@ async def test_mantle_anthropic_messages_streaming_sends_stream_and_passes_throu
assert "event: message_start" in text
assert '"text": "pong"' in text
assert "event: message_stop" in text
+
+
+@pytest.mark.usefixtures("local_beta_headers_config")
+def test_mantle_messages_drops_compaction_without_its_beta():
+ """
+ The bedrock_mantle beta map has no compact-2026-09-04, so the beta is filtered
+ out on this route. ``compaction`` must go with it, or the body carries a field
+ that Bedrock refuses without the beta.
+ """
+ from litellm.types.router import GenericLiteLLMParams
+
+ config = AmazonMantleMessagesConfig()
+ headers: dict = {}
+ request = config.transform_anthropic_messages_request(
+ model="mantle/anthropic.claude-mythos-preview",
+ messages=[{"role": "user", "content": "Hello"}],
+ anthropic_messages_optional_request_params={"max_tokens": 100, "compaction": {"type": "summarize"}},
+ litellm_params=GenericLiteLLMParams(),
+ headers=headers,
+ )
+
+ assert "compaction" not in request
+ assert "compact-2026-09-04" not in headers.get("anthropic-beta", "")
diff --git a/tests/unit/test_anthropic_beta_headers_filtering.py b/tests/unit/test_anthropic_beta_headers_filtering.py
index a9bc741f800..d0fb6fe0f6e 100644
--- a/tests/unit/test_anthropic_beta_headers_filtering.py
+++ b/tests/unit/test_anthropic_beta_headers_filtering.py
@@ -99,7 +99,9 @@ class TestAnthropicBetaHeadersFiltering:
filtered = filter_and_transform_beta_headers(
beta_headers=all_headers, provider=provider
)
- assert ("compact-2026-09-04" in filtered) is (provider in ("anthropic", "azure_ai", "vertex_ai"))
+ # Bedrock InvokeModel serves the compact-2026-09-04 `compaction` parameter; Converse does not.
+ # https://docs.aws.amazon.com/bedrock/latest/userguide/claude-messages-compaction.html
+ assert ("compact-2026-09-04" in filtered) is (provider in ("anthropic", "azure_ai", "vertex_ai", "bedrock"))
assert ("mid-conversation-tool-changes-2026-07-01" in filtered) is (provider != "bedrock_converse")
for header in unsupported_headers: