litellm - ✅(Solved) Fix [Bug]: `/v1/messages` endpoint does not sanitize empty text content blocks [5 pull requests, 3 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
BerriAI/litellm#22930Fetched 2026-04-08 00:39:21
View on GitHub
Comments
3
Participants
3
Timeline
27
Reactions
0
Timeline (top)
referenced ×9cross-referenced ×7commented ×3labeled ×3

Error Message

400: {"type":"error","error":{"type":"invalid_request_error","message":"messages: text content blocks must be non-empty"}} Bedrock Converse), causing the 400 error.

Root Cause

This happens because Claude's API returns assistant messages with empty text blocks alongside tool_use blocks:

  {
    "role": "assistant",
    "content": [
      {"type": "text", "text": ""},
      {"type": "tool_use", "id": "toolu_xxx", "name": "Bash", "input": {...}}
    ]
  }

The Anthropic API returns these but rejects them when sent back in subsequent requests (see anthropics/anthropic-sdk-python#461). When using the /anthropic passthrough endpoint, the Anthropic SDK handles this transparently. But the /v1/messages native path (used for Anthropic direct and Bedrock) passes messages through without sanitization, so empty text blocks from previous responses get forwarded as-is and get sent back to Anthropic (or Bedrock Converse), causing the 400 error.

Fix Action

Fixed

PR fix notes

PR #22933: fix: sanitize empty text content blocks in /v1/messages endpoint

Description (problem / solution / changelog)

Summary

  • Sanitize empty text content blocks in the /v1/messages endpoint before forwarding to providers
  • Fixes 400 errors ("text content blocks must be non-empty") in multi-turn tool-use conversations

Problem

The Anthropic API returns assistant messages with empty text blocks alongside tool_use blocks:

{"type": "text", "text": ""},
{"type": "tool_use", "id": "toolu_xxx", "name": "Bash", "input": {...}}

While the API returns these, it rejects them when sent back in subsequent requests. The /anthropic passthrough handles this transparently, but the /v1/messages native path (used for Anthropic direct and Bedrock) passes messages through without sanitization.

The /v1/chat/completions path already handles this via process_empty_text_blocks() in factory.py, but the /v1/messages path was missing equivalent sanitization. modify_params=True does not help as it targets a different code path.

Fix

Add _sanitize_anthropic_messages() at the entry point of anthropic_messages() in the /v1/messages handler, before any routing. This strips empty/whitespace-only text blocks from message content arrays while preserving non-empty text and other block types (tool_use, tool_result, etc.). Does not leave content arrays empty to avoid a different validation error.

Testing

  • 7 unit tests covering: empty text + tool_use, whitespace-only, non-empty preservation, single empty block (no modification), string content, and user messages
  • Verified fix on production deployment with Claude Code / Agent SDK multi-turn tool-use conversations routed through /v1/messages to both Anthropic direct and Bedrock Converse

Fixes #22930

Changed files

  • litellm/llms/anthropic/experimental_pass_through/messages/handler.py (modified, +60/-0)
  • tests/test_litellm/llms/anthropic/experimental_pass_through/messages/test_anthropic_experimental_pass_through_messages_handler.py (modified, +146/-0)

PR #22979: fix: sanitize empty text content blocks in /v1/messages endpoint

Description (problem / solution / changelog)

Problem

Multi-turn tool-use conversations through /v1/messages fail with:

400: messages: text content blocks must be non-empty

Claude's API returns assistant messages with empty text blocks alongside tool_use blocks ({"type": "text", "text": ""}). These are valid in responses but rejected when sent back. The /v1/messages endpoint passes messages through without sanitizing.

Empty text sanitization already exists for /v1/chat/completions (Bedrock #7177, Anthropic #20370, Databricks #20384) but was missing from the /v1/messages code path.

Fix

Add _sanitize_anthropic_empty_text_blocks() in the /v1/messages handler to strip empty text content blocks before forwarding to the upstream API.

Fixes #22930

Changed files

  • litellm/llms/anthropic/experimental_pass_through/messages/handler.py (modified, +39/-0)

PR #23097: fix: strip empty text content blocks in /v1/messages endpoint

Description (problem / solution / changelog)

Summary

Fixes #22930

When routing Anthropic SDK requests through LiteLLM's /v1/messages endpoint, multi-turn tool-use conversations fail because Claude's API returns assistant messages with empty text blocks ({"type": "text", "text": ""}) alongside tool_use blocks. These empty blocks are forwarded as-is and rejected when sent back with:

400: {"type":"error","error":{"type":"invalid_request_error","message":"messages: text content blocks must be non-empty"}}

Sanitization already exists for other code paths:

  • Bedrock via /v1/chat/completions — #7169, PR #7177
  • Anthropic via /v1/chat/completions — #20370
  • Databricks — #20370, PR #20384
  • modify_params=True in anthropic_messages_pt — #19061, PR #21464

But none of these fixes cover the /v1/messages native path, which passes messages through async_anthropic_messages_handlertransform_anthropic_messages_request without any sanitization.

Changes

  • Added _sanitize_anthropic_messages_empty_text_blocks() helper in litellm/llms/custom_httpx/llm_http_handler.py that strips empty/whitespace-only text blocks from Anthropic-format message content arrays before forwarding
  • Called it in async_anthropic_messages_handler() right before transform_anthropic_messages_request(), so all providers using the native /v1/messages path benefit (Anthropic direct, Bedrock invoke, Azure AI, Vertex AI)
  • If stripping would leave an empty content array (all blocks were empty text), replaces with a single {"type": "text", "text": "..."} placeholder

Test plan

  • Added tests/test_litellm/llms/anthropic/test_v1_messages_empty_text_sanitization.py with 10 unit tests covering:
    • Empty text block alongside tool_use is stripped
    • Non-empty text blocks are preserved
    • Whitespace-only text blocks are stripped
    • All-empty content replaced with placeholder (not empty array)
    • String content messages pass through unchanged
    • Messages without content key pass through unchanged
    • User message content lists are also sanitized
    • tool_result blocks are not affected
    • Multi-message end-to-end scenario
    • Original messages are not mutated

Changed files

  • litellm/llms/custom_httpx/llm_http_handler.py (modified, +60/-0)
  • tests/test_litellm/llms/anthropic/test_v1_messages_empty_text_sanitization.py (added, +247/-0)

PR #23232: Revert "fix: strip empty text content blocks in /v1/messages endpoint"

Description (problem / solution / changelog)

Reverts BerriAI/litellm#23097

Breaks test_bad_request_error_handling_streaming

Changed files

  • litellm/llms/custom_httpx/llm_http_handler.py (modified, +0/-60)
  • tests/test_litellm/llms/anthropic/test_v1_messages_empty_text_sanitization.py (removed, +0/-247)

PR #23231: Fix logging tests

Description (problem / solution / changelog)

Relevant issues

<!-- e.g. "Fixes #000" -->

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Type

<!-- Select the type of Pull Request --> <!-- Keep only the necessary ones -->

🆕 New Feature 🐛 Bug Fix 🧹 Refactoring 📖 Documentation 🚄 Infrastructure ✅ Test

Changes

Changed files

  • litellm/litellm_core_utils/redact_messages.py (modified, +10/-0)
  • litellm/llms/custom_httpx/llm_http_handler.py (modified, +0/-60)
  • litellm/main.py (modified, +8/-0)
  • provider_endpoints_support.json (modified, +18/-0)
  • tests/test_litellm/llms/anthropic/test_v1_messages_empty_text_sanitization.py (removed, +0/-247)
  • tests/test_litellm/llms/openai/chat/test_store_param.py (added, +188/-0)

Code Example

{
    "role": "assistant",
    "content": [
      {"type": "text", "text": ""},
      {"type": "tool_use", "id": "toolu_xxx", "name": "Bash", "input": {...}}
    ]
  }

---

model_list:
    - model_name: claude-opus-4-5
      litellm_params:
        model: anthropic/claude-opus-4-5-20251101
        api_key: os.environ/ANTHROPIC_API_KEY

    - model_name: claude-opus-4-5
      litellm_params:
        model: bedrock/converse/us.anthropic.claude-opus-4-5-20251101-v1:0

  router_settings:
    routing_strategy: latency-based-routing

  litellm_settings:
    modify_params: true  # Does not fix this issue

---
RAW_BUFFERClick to expand / collapse

Check for existing issues

  • I have searched the existing issues and checked that my issue is not a duplicate.

What happened?

What happened?

When routing Anthropic SDK requests through LiteLLM's /v1/messages router endpoint (not the /anthropic passthrough), multi-turn tool-use conversations fail with:

400: {"type":"error","error":{"type":"invalid_request_error","message":"messages: text content blocks must be non-empty"}}

This happens because Claude's API returns assistant messages with empty text blocks alongside tool_use blocks:

  {
    "role": "assistant",
    "content": [
      {"type": "text", "text": ""},
      {"type": "tool_use", "id": "toolu_xxx", "name": "Bash", "input": {...}}
    ]
  }

The Anthropic API returns these but rejects them when sent back in subsequent requests (see anthropics/anthropic-sdk-python#461). When using the /anthropic passthrough endpoint, the Anthropic SDK handles this transparently. But the /v1/messages native path (used for Anthropic direct and Bedrock) passes messages through without sanitization, so empty text blocks from previous responses get forwarded as-is and get sent back to Anthropic (or Bedrock Converse), causing the 400 error.

Relevant issues

Empty text block sanitization already exists for:

  • Bedrock via /v1/chat/completions — #7169, fixed in PR #7177
  • Anthropic via /v1/chat/completions — confirmed fixed in #20370
  • Databricks — #20370, fixed in PR #20384
  • modify_params=True sanitization in anthropic_messages_pt — #19061, PR #21464

None of these fixes cover the /v1/messages router code path. modify_params=True does not help.

Steps to Reproduce

  1. Configure LiteLLM with an Anthropic model in model_list
  2. Use the Anthropic SDK with base_url pointing at LiteLLM's root (so requests hit /v1/messages, not /anthropic/v1/messages)
  3. Run a multi-turn conversation with tool use (e.g., using @anthropic-ai/claude-agent-sdk / Claude Code)
  4. After several turns, the conversation history will contain assistant messages with {"type": "text", "text": ""} alongside tool_use blocks
  5. The next API call fails with 400 "text content blocks must be non-empty"

Config:

  model_list:
    - model_name: claude-opus-4-5
      litellm_params:
        model: anthropic/claude-opus-4-5-20251101
        api_key: os.environ/ANTHROPIC_API_KEY

    - model_name: claude-opus-4-5
      litellm_params:
        model: bedrock/converse/us.anthropic.claude-opus-4-5-20251101-v1:0

  router_settings:
    routing_strategy: latency-based-routing

  litellm_settings:
    modify_params: true  # Does not fix this issue

Expected behavior

The /v1/messages endpoint should sanitize empty text content blocks (strip {"type": "text", "text": ""} from content arrays, or replace with a non-empty placeholder) before forwarding to providers, matching the behavior already implemented for /v1/chat/completions.

Relevant log output

What part of LiteLLM is this about?

Proxy

What LiteLLM version are you on ?

v1.81.14-stable

Twitter / LinkedIn details

No response

extent analysis

Fix Plan

To fix the issue of empty text content blocks being forwarded to Anthropic API, we need to sanitize the messages before sending them to the provider. We can achieve this by modifying the /v1/messages router endpoint to remove or replace empty text blocks.

Here are the steps to fix the issue:

  • Modify the /v1/messages router endpoint to sanitize messages
  • Remove or replace empty text blocks from the content array

Example code snippet in Python:

def sanitize_message(message):
    """Remove empty text blocks from the content array"""
    sanitized_content = [block for block in message["content"] if block["type] != text" or block["text"] != ""]
    message = sanitized_content
    return message

def process_message(message):
    """Sanitize the message before forwarding to the provider"""
    sanitized_message = sanitize_message(message)
    # Forward the sanitized message to the provider
    return sanitized_message

Alternatively, you can replace empty text blocks with a non-empty placeholder:

def sanitize_message(message):
    """Replace empty text blocks with a non-empty placeholder"""
    sanitized_content = []
    for block in message["content"]:
        if block["type"] ==text and block["text"] == "":
            block = "[EMPTY]"  # Replace with a non-empty placeholder
        sanitized_content.append(block)
    message["content"] = sanitized_content
    return message

Verification

To verify that the fix worked, you can test the /v1/messages endpoint with a message containing an empty text block. The endpoint should return a sanitized message with the empty text block removed or replaced.

You can also test a multi-turn conversation with tool use to ensure that the conversation history does not contain empty text blocks.

Extra Tips

  • Make sure to update the /v1/messages router endpoint to use the sanitize_message function before forwarding messages to the provider.
  • Consider adding a configuration option to enable or disable message sanitization.
  • You can also add logging to track the number of sanitized messages and the type of sanitization applied.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

litellm - ✅(Solved) Fix [Bug]: `/v1/messages` endpoint does not sanitize empty text content blocks [5 pull requests, 3 comments, 3 participants]