From c676e0c134173c17f4038f93ce75c463bba0063c Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Wed, 11 Feb 2026 14:18:57 +0530 Subject: [PATCH] Fix: remove x-anthropic-billing block --- litellm/llms/anthropic/chat/transformation.py | 7 +++ .../messages/transformation.py | 45 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/litellm/llms/anthropic/chat/transformation.py b/litellm/llms/anthropic/chat/transformation.py index 82aa7390188..0c7dfc13dbc 100644 --- a/litellm/llms/anthropic/chat/transformation.py +++ b/litellm/llms/anthropic/chat/transformation.py @@ -931,6 +931,7 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): Translate system message to anthropic format. Removes system message from the original list and returns a new list of anthropic system message content. + Filters out system messages containing x-anthropic-billing-header metadata. """ system_prompt_indices = [] anthropic_system_message_list: List[AnthropicSystemMessageContent] = [] @@ -942,6 +943,9 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): # Skip empty text blocks - Anthropic API raises errors for empty text if not system_message_block["content"]: continue + # Skip system messages containing x-anthropic-billing-header metadata + if system_message_block["content"].startswith("x-anthropic-billing-header:"): + continue anthropic_system_message_content = AnthropicSystemMessageContent( type="text", text=system_message_block["content"], @@ -960,6 +964,9 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig): text_value = _content.get("text") if _content.get("type") == "text" and not text_value: continue + # Skip system messages containing x-anthropic-billing-header metadata + if _content.get("type") == "text" and text_value and text_value.startswith("x-anthropic-billing-header:"): + continue anthropic_system_message_content = ( AnthropicSystemMessageContent( type=_content.get("type"), diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/transformation.py b/litellm/llms/anthropic/experimental_pass_through/messages/transformation.py index 043a70f3c67..4b0fb14a824 100644 --- a/litellm/llms/anthropic/experimental_pass_through/messages/transformation.py +++ b/litellm/llms/anthropic/experimental_pass_through/messages/transformation.py @@ -52,6 +52,40 @@ class AnthropicMessagesConfig(BaseAnthropicMessagesConfig): # TODO: Add Anthropic `metadata` support # "metadata", ] + + @staticmethod + def _filter_billing_headers_from_system(system_param): + """ + Filter out x-anthropic-billing-header metadata from system parameter. + + Args: + system_param: Can be a string or a list of system message content blocks + + Returns: + Filtered system parameter (string or list), or None if all content was filtered + """ + if isinstance(system_param, str): + # If it's a string and starts with billing header, filter it out + if system_param.startswith("x-anthropic-billing-header:"): + return None + return system_param + elif isinstance(system_param, list): + # Filter list of system content blocks + filtered_list = [] + for content_block in system_param: + if isinstance(content_block, dict): + text = content_block.get("text", "") + content_type = content_block.get("type", "") + # Skip text blocks that start with billing header + if content_type == "text" and text.startswith("x-anthropic-billing-header:"): + continue + filtered_list.append(content_block) + else: + # Keep non-dict items as-is + filtered_list.append(content_block) + return filtered_list if len(filtered_list) > 0 else None + else: + return system_param def get_complete_url( self, @@ -123,6 +157,17 @@ class AnthropicMessagesConfig(BaseAnthropicMessagesConfig): message="max_tokens is required for Anthropic /v1/messages API", status_code=400, ) + + # Filter out x-anthropic-billing-header from system messages + system_param = anthropic_messages_optional_request_params.get("system") + if system_param is not None: + filtered_system = self._filter_billing_headers_from_system(system_param) + if filtered_system is not None and len(filtered_system) > 0: + anthropic_messages_optional_request_params["system"] = filtered_system + else: + # Remove system parameter if all content was filtered out + anthropic_messages_optional_request_params.pop("system", None) + ####### get required params for all anthropic messages requests ###### verbose_logger.debug(f"TRANSFORMATION DEBUG - Messages: {messages}") anthropic_messages_request: AnthropicMessagesRequest = AnthropicMessagesRequest(