Skip to content

Refactor: Filtering beta header after transformation#23715

Merged
Sameerlite merged 1 commit intomainfrom
litellm_anthropic_beta_header_order
Mar 16, 2026
Merged

Refactor: Filtering beta header after transformation#23715
Sameerlite merged 1 commit intomainfrom
litellm_anthropic_beta_header_order

Conversation

@Sameerlite
Copy link
Collaborator

Relevant issues

Unsupported anthropic-beta headers sent to Vertex AI for Anthropic models
Affected versions: At least 1.82.1 (likely earlier)
Symptom:
Requests to Vertex AI Anthropic models (e.g. vertex_ai/claude-sonnet-4-6@default) fail with:
Unexpected value(s) code-execution-2025-05-22, files-api-2025-04-14
for the anthropic-beta header.

This happens when messages contain file IDs ("source": {"type": "file", "file_id": ...}), because LiteLLM auto-detects the feature and adds the corresponding beta headers — headers that Vertex AI does not support.

Root cause:
VertexAIAnthropicConfig.transform_request() (in litellm/llms/vertex_ai/vertex_ai_partner_models/anthropic/transformation.py) builds a beta header set by calling the shared get_anthropic_beta_list() method, which unconditionally adds files-api-2025-04-14 and code-execution-2025-05-22 when file IDs are detected. This set is written directly to both data["anthropic_beta"] (request body) and headers["anthropic-beta"] (HTTP header) without passing through filter_and_transform_beta_headers().

The filtering infrastructure already exists — anthropic_beta_headers_config.json correctly marks files-api-2025-04-14 as null (unsupported) for vertex_ai, and code-execution-2025-05-22 isn't in the mapping at all (so it would also be dropped). The problem is that this filtering is never invoked on the betas generated inside transform_request():
In handler.py, update_headers_with_filtered_beta() runs before transform_request(), so its filtering gets overwritten.

In llm_http_handler.py, filtering only covers the anthropic messages pass-through path, not the standard completion path.

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

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-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

🐛 Bug Fix

Changes

Moved beta header filtering to after transform_request() in:

litellm/llms/anthropic/chat/handler.py (lines 362-377) - For the Anthropic-specific handler path (which Vertex AI Claude uses)
Now:
Filter headers["anthropic-beta"] using update_headers_with_filtered_beta()
Filter data["anthropic_beta"] (request body) using filter_and_transform_beta_headers()
This ensures that all beta headers—whether user-provided or auto-detected—are filtered based on the provider's support matrix in anthropic_beta_headers_config.json, where files-api-2025-04-14 is marked as null (unsupported) for vertex_ai, and code-execution-2025-05-22 isn't in the mapping at all (so it gets dropped).

Before
image

After
image

@vercel
Copy link

vercel bot commented Mar 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Mar 16, 2026 5:23am

Request Review

@codspeed-hq
Copy link
Contributor

codspeed-hq bot commented Mar 16, 2026

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing litellm_anthropic_beta_header_order (1a8f8c6) with main (548e7eb)

Open in CodSpeed

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 16, 2026

Greptile Summary

This PR fixes a bug where unsupported anthropic-beta headers (e.g. files-api-2025-04-14, code-execution-2025-05-22) were being forwarded to Vertex AI when auto-detected by VertexAIAnthropicConfig.transform_request(). The fix moves the beta-header filtering call to after transform_request() in handler.py, ensuring both the HTTP anthropic-beta header and the anthropic_beta request-body field are sanitised regardless of how the betas were injected.

Key changes:

  • anthropic_beta_headers_manager.py: New update_request_with_filtered_beta() helper that filters both the HTTP header and request body in one call.
  • handler.py: Replaces the pre-transform_request call to update_headers_with_filtered_beta with a post-transform_request call to the new combined helper, so auto-detected betas are correctly filtered.
  • Test added for the utility function; however, no end-to-end regression test exercises the actual Vertex AI path with file-ID-triggered auto-detection.

Confidence Score: 4/5

  • Safe to merge with minor caveats — the core fix is correct and well-scoped.
  • The root-cause analysis is accurate and the fix cleanly addresses the reported issue by moving filtering to after transform_request(). The new utility function works correctly for the List[str] case that all current callers produce. A small type-safety gap exists if anthropic_beta happens to be a string in request_data, and the test suite lacks an end-to-end regression that would catch a re-introduction of the same bug.
  • Pay attention to litellm/anthropic_beta_headers_manager.py — the new update_request_with_filtered_beta function should guard against a string-typed anthropic_beta body value.

Important Files Changed

Filename Overview
litellm/anthropic_beta_headers_manager.py Adds update_request_with_filtered_beta — a composite helper that filters both the anthropic-beta HTTP header and the anthropic_beta request-body field. Logic is correct for the list-based case; minor type-safety gap if anthropic_beta body value is a raw string.
litellm/llms/anthropic/chat/handler.py Moves beta-header filtering to after transform_request(), so auto-detected betas (e.g. files-api-2025-04-14) added by the Vertex AI transformer are now properly filtered. Import is also cleaned up from its previous location.
tests/test_litellm/test_anthropic_beta_headers_filtering.py Adds a unit test for the new utility function with correct assertions. Misses an end-to-end regression test that exercises the actual Vertex AI transform path with auto-detected file-ID betas.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant handler as AnthropicChatCompletion.completion()
    participant transform as VertexAIAnthropicConfig.transform_request()
    participant filter as update_request_with_filtered_beta()
    participant config as anthropic_beta_headers_config.json

    Caller->>handler: completion(model="vertex_ai/...", messages=[...file_id...])
    handler->>transform: transform_request(headers, optional_params)
    transform->>transform: get_anthropic_beta_list() → adds files-api-2025-04-14, code-execution-2025-05-22
    transform->>transform: data["anthropic_beta"] = list(beta_set)
    transform->>transform: headers["anthropic-beta"] = ",".join(beta_set)
    transform-->>handler: returns data (with unfiltered betas)
    handler->>filter: update_request_with_filtered_beta(headers, data, "vertex_ai")
    filter->>config: load provider mapping for vertex_ai
    config-->>filter: files-api-2025-04-14 → null, code-execution-2025-05-22 → (unknown)
    filter->>filter: drop unsupported/unknown headers from both HTTP header and body
    filter-->>handler: (filtered headers, filtered data)
    handler->>Caller: HTTP POST with only supported betas
Loading

Last reviewed commit: 1a8f8c6

Comment on lines +389 to +396
existing_body_betas = request_data.get("anthropic_beta")
if not existing_body_betas:
return headers, request_data

filtered_body_betas = filter_and_transform_beta_headers(
beta_headers=existing_body_betas,
provider=provider,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing type guard for anthropic_beta string value

filter_and_transform_beta_headers expects a List[str], but request_data["anthropic_beta"] could be a plain comma-separated string if the caller passes it through optional_params directly. If it is a string, for header in beta_headers: will iterate over individual characters ("f", "i", "l", "e", …) — none of which exist in the provider mapping — silently dropping all beta headers.

This is unlikely to occur in the current Vertex AI path (where transform_request always stores it as a list), but the function is a public utility and accepts dict without type constraints, making it brittle for future callers.

    existing_body_betas = request_data.get("anthropic_beta")
    if not existing_body_betas:
        return headers, request_data

    # Normalise: handle both list and comma-separated string
    if isinstance(existing_body_betas, str):
        existing_body_betas = [b.strip() for b in existing_body_betas.split(",") if b.strip()]

    filtered_body_betas = filter_and_transform_beta_headers(
        beta_headers=existing_body_betas,
        provider=provider,
    )

Comment on lines +120 to +144
def test_update_request_with_filtered_beta_vertex_ai(self):
"""Test combined filtering for both HTTP headers and request body betas."""
headers = {
"anthropic-beta": "files-api-2025-04-14,context-management-2025-06-27,code-execution-2025-05-22"
}
request_data = {
"anthropic_beta": [
"files-api-2025-04-14",
"context-management-2025-06-27",
"code-execution-2025-05-22",
]
}

filtered_headers, filtered_request_data = update_request_with_filtered_beta(
headers=headers,
request_data=request_data,
provider="vertex_ai",
)

assert (
filtered_headers.get("anthropic-beta") == "context-management-2025-06-27"
)
assert filtered_request_data.get("anthropic_beta") == [
"context-management-2025-06-27"
]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test covers utility, not the actual bug path

The new test validates update_request_with_filtered_beta in isolation, which is useful. However, the root cause described in the PR is that auto-detected beta headers (injected by get_anthropic_beta_list inside VertexAIAnthropicConfig.transform_request) were never filtered. A more targeted regression test would simulate the real scenario:

  • Call litellm.completion (or the relevant handler directly) with a vertex_ai model and messages that contain a file_id source block.
  • Assert that neither data["anthropic_beta"] nor headers["anthropic-beta"] in the captured HTTP request contain files-api-2025-04-14 or code-execution-2025-05-22.

Without this, a future refactor that re-introduces the same omission would not be caught by the test suite.

@Sameerlite Sameerlite merged commit 0bbdd2a into main Mar 16, 2026
93 of 99 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants