From d19d83925cea15ee6ff2c44b1cc29a1faf945909 Mon Sep 17 00:00:00 2001 From: factnn <166481866+factnn@users.noreply.github.com> Date: Sun, 21 Jun 2026 15:48:17 +0800 Subject: [PATCH] fix: strip extra fields from messages for Mistral chat Mistral API enforces additionalProperties: false on its message schema, rejecting extra fields like metadata, provider_specific_fields, thinking_blocks, and cache_control. Strip these before sending. Fixes #30882 --- litellm/llms/mistral/chat/transformation.py | 7 +++++ .../test_mistral_chat_transformation.py | 30 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/litellm/llms/mistral/chat/transformation.py b/litellm/llms/mistral/chat/transformation.py index 0f202a22c96..9de93ef94a6 100644 --- a/litellm/llms/mistral/chat/transformation.py +++ b/litellm/llms/mistral/chat/transformation.py @@ -265,6 +265,13 @@ class MistralConfig(OpenAIGPTConfig): if MistralConfig._is_empty_assistant_message(m): continue m = strip_none_values_from_message(m) # prevents 'extra_forbidden' error + # GH#30882: strip fields not permitted by Mistral + # (additionalProperties: false on their message schema) + if isinstance(m, dict): + m.pop("metadata", None) + m.pop("provider_specific_fields", None) + m.pop("thinking_blocks", None) + m.pop("cache_control", None) new_messages.append(m) if is_async: diff --git a/tests/test_litellm/llms/mistral/test_mistral_chat_transformation.py b/tests/test_litellm/llms/mistral/test_mistral_chat_transformation.py index 7a3f372582f..3b54aa18a3c 100644 --- a/tests/test_litellm/llms/mistral/test_mistral_chat_transformation.py +++ b/tests/test_litellm/llms/mistral/test_mistral_chat_transformation.py @@ -249,6 +249,36 @@ class TestMistralReasoningSupport: assert result == messages assert len(result) == 1 + def test_transform_messages_strips_extra_fields(self): + """GH#30882: extra fields like metadata should be stripped + from messages before sending to Mistral API.""" + mistral_config = MistralConfig() + messages: List[AllMessageValues] = [ + cast( + AllMessageValues, + { + "role": "assistant", + "content": "hello", + "metadata": { + "tool_outputs_trimmed": True, + "trimmed_by": "async_context_compression", + }, + "provider_specific_fields": {"foo": "bar"}, + "thinking_blocks": [], + "cache_control": {"type": "ephemeral"}, + }, + ) + ] + result = mistral_config._transform_messages(messages, "mistral/mistral-large-latest") + assert len(result) == 1 + msg = cast(dict, result[0]) + assert msg["role"] == "assistant" + assert msg["content"] == "hello" + assert "metadata" not in msg + assert "provider_specific_fields" not in msg + assert "thinking_blocks" not in msg + assert "cache_control" not in msg + def test_transform_request_magistral_with_reasoning(self): """Test transform_request method for magistral model with reasoning.""" mistral_config = MistralConfig()