Skip to content

fix(responses): backport request-param leak fixes to rc/1.102.0 (#41018, #41141, #41144) - #41342

Merged
yuneng-berri merged 3 commits into
rc/1.102.0from
litellm_backport_param_leaks_rc_1_102_0
Sep 16, 2026
Merged

yuneng-berri merged 3 commits into
rc/1.102.0from
litellm_backport_param_leaks_rc_1_102_0

Conversation

@yuneng-berri

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • litellm's own bookkeeping keys reach provider request bodies
  • Bridged /v1/responses forwards params the native path drops
  • Forwarded client headers land in the JSON body

How it solves it:

  • Registers the internal key names so they get stripped
  • Filters bridged kwargs the same way native dispatch does
  • Keeps extra_headers out of the body on the httpx path

Straight backport of three PRs already merged to main, onto the 1.102.0 release line so they ship in 1.102.0 stable: #41018, #41141, #41144

User Flow

Before: a developer calling the gateway's Responses endpoint gets a 400 naming a parameter they never sent

  1. Their gateway config stamps chat_template_kwargs onto the gpt-5.4 deployment, and their load balancer adds x-forwarded-for to every request
  2. They send POST https://litellm-domain/v1/responses with {"model": "gpt-5.4", "input": "Reply with exactly one word: hello"} and no extra parameters
  3. The call comes back 400 OpenAIException - Unknown parameter: 'chat_template_kwargs'
  4. With header forwarding on, the same endpoint returns 400 Unknown parameter: 'extra_headers'
  5. On a gpt-5.4 request carrying function tools, it returns 400 Unknown parameter: 'model_alias_map'
  6. The same three requests succeed against /v1/chat/completions, so the failure depends on which endpoint they call, not on what they sent

After: the same requests come back 200, and the provider only ever sees parameters the caller actually chose

  1. Same config, same load balancer, same deployment
  2. They send the same POST https://litellm-domain/v1/responses request
  3. It returns 200 with the model's answer
  4. Header forwarding still works: x-forwarded-for reaches the provider as an HTTP header, not as a body field
  5. The gpt-5.4 tools request returns 200
  6. A caller who genuinely wants a provider-specific parameter forwarded asks for it by name with "allowed_openai_params": ["chat_template_kwargs"], and it reaches the provider

Relevant issues

Linear ticket

Resolves LIT-7695
Resolves LIT-7711
Resolves LIT-7694

Pre-Submission checklist

  • I have added meaningful tests
  • The handful of test files covering my change pass locally, e.g. uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*, make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

Screenshots / Proof of Fix

Live proxy on both sides, same config, real calls to the xAI API. The check is what the gateway actually puts on the wire to the provider, read out of the proxy's own debug log

Shared setup, identical for both runs:

model_list:
  - model_name: grok-bridge
    litellm_params:
      model: xai/grok-4.6
      api_key: os.environ/XAI_API_KEY

general_settings:
  master_key: sk-1234
  forward_client_headers_to_llm_api: true

litellm_settings:
  drop_params: false
LITELLM_LOG=DEBUG python litellm/proxy/proxy_cli.py --config rc2_proof_config.yaml --port 4001

Before (16f6d92)

A bridged Responses request leaks the caller's unknown param to the provider

  1. Send the request
curl -sS -w '\nHTTP %{http_code}\n' http://localhost:4001/v1/responses \
  -H 'Authorization: Bearer sk-1234' -H 'Content-Type: application/json' \
  -d '{"model":"grok-bridge","input":"Reply with exactly one word: hello","use_chat_completions_api":true,"chat_template_kwargs":{"thinking":true}}'
HTTP 200
  1. Read what the proxy actually sent upstream
POST Request Sent from LiteLLM:
curl -X POST \
https://api.x.ai/v1/chat/completions \
-H 'Authorization: Be****D9' -H 'Content-Type: application/json' \
-d '{'model': 'grok-4.6', 'messages': [{'role': 'user', 'content': 'Reply with exactly one word: hello'}], 'chat_template_kwargs': {'thinking': True}}'

chat_template_kwargs is in the body. A provider that validates its request schema rejects this with a 400

After (262978e)

A bridged Responses request leaks the caller's unknown param to the provider

  1. Send the identical request
curl -sS -w '\nHTTP %{http_code}\n' http://localhost:4001/v1/responses \
  -H 'Authorization: Bearer sk-1234' -H 'Content-Type: application/json' \
  -d '{"model":"grok-bridge","input":"Reply with exactly one word: hello","use_chat_completions_api":true,"chat_template_kwargs":{"thinking":true}}'
HTTP 200
  1. Read what the proxy sent upstream
POST Request Sent from LiteLLM:
curl -X POST \
https://api.x.ai/v1/chat/completions \
-H 'Authorization: Be****D9' -H 'Content-Type: application/json' \
-d '{'model': 'grok-4.6', 'messages': [{'role': 'user', 'content': 'Reply with exactly one word: hello'}]}'

The param is gone, matching what native Responses dispatch already did

  1. Ask for it explicitly and it comes back
curl -sS -w '\nHTTP %{http_code}\n' http://localhost:4001/v1/responses \
  -H 'Authorization: Bearer sk-1234' -H 'Content-Type: application/json' \
  -d '{"model":"grok-bridge","input":"Reply with exactly one word: hello","use_chat_completions_api":true,"chat_template_kwargs":{"thinking":true},"allowed_openai_params":["chat_template_kwargs"]}'
HTTP 200

POST Request Sent from LiteLLM:
curl -X POST \
https://api.x.ai/v1/chat/completions \
-H 'Authorization: Be****D9' -H 'Content-Type: application/json' \
-d '{'model': 'grok-4.6', 'messages': [{'role': 'user', 'content': 'Reply with exactly one word: hello'}], 'chat_template_kwargs': {'thinking': True}}'

Type

🐛 Bug Fix

Caveats (if any)

Medium

  • Bridged /v1/responses now drops unknown provider params
    • A request that worked by leaning on bridge passthrough stops sending that param
    • Verified live: dataSources on an Azure deployment reaches the provider at 16f6d92 and does not at 262978e
    • The caller gets it back by naming it in allowed_openai_params
    • Not new here: main already behaves this way, this backport just brings the line in line

Low

  • The 400 in the user flow is OpenAI's wording; other providers accept the stray param
    • The live run above uses xAI, which returns 200 either way, so the proof reads the outgoing body instead of the status code
    • The two body-leak fixes for extra_headers and model_alias_map are covered by tests, not by the live run above, because they only surface on an OpenAI-family provider

fix(utils): keep litellm params out of provider request bodies

(cherry picked from commit 1f46e58)
…ers_body_leak

fix(openai): keep extra_headers out of the chat request body on the httpx handler path

(cherry picked from commit de55e22)
…rs_unknown_params

fix(responses): filter bridged kwargs like the native Responses path

(cherry picked from commit 41b5d47)
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@yuneng-berri
yuneng-berri merged commit 3f96566 into rc/1.102.0 Sep 16, 2026
8 checks passed
@yuneng-berri
yuneng-berri deleted the litellm_backport_param_leaks_rc_1_102_0 branch September 16, 2026 01:06
@greptile-apps

greptile-apps Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This backport aligns bridged Responses parameter filtering with native dispatch, registers additional internal parameters, and keeps forwarded headers out of OpenAI request bodies

  • Filters bridged kwargs while preserving explicitly allowed provider parameters
  • Prevents extra_headers from entering JSON bodies on the base HTTP path
  • Adds regression coverage for filtering, credentials, headers, streaming, and internal parameters
  • Leaves a GitHub Copilot header-forwarding regression when the base HTTP handler is enabled

Confidence Score: 4/5

The PR needs changes before merging because GitHub Copilot custom headers are dropped on the experimental base-handler path and an explicit comment rule is violated

The main fixes are well covered, but the changed dispatch loses user-provided Copilot headers whenever the base HTTP handler is enabled

Files Needing Attention: litellm/main.py, tests/test_litellm/test_utils.py

Important Files Changed
Filename Overview
litellm/main.py Separates base-handler headers from body parameters, but drops user extra headers for GitHub Copilot
litellm/responses/main.py Filters bridged Responses kwargs using supported, internal, generic, and explicitly allowed parameter sets
litellm/types/utils.py Registers additional LiteLLM bookkeeping fields so provider parameter construction strips them
tests/test_litellm/responses/test_responses_api_bridge_flag.py Adds mocked wire-level coverage for bridge filtering and deployment credentials
tests/test_litellm/test_main.py Covers sync and streaming header forwarding through the base OpenAI HTTP handler
tests/test_litellm/test_utils.py Adds internal-parameter leak coverage but includes a disallowed explanatory docstring

Reviews (1): Last reviewed commit: "Merge pull request #41144 from BerriAI/l..." | Re-trigger Greptile

Comment thread litellm/main.py
Comment on lines +2597 to 2598
if extra_headers is not None and not use_base_llm_http_handler:
optional_params["extra_headers"] = extra_headers

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Copilot headers are dropped When the base HTTP handler is enabled, user extra headers stay separate from dispatched headers and never reach the provider

Knowledge Base Used: Model invocation runtime

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +5279 to +5282
"""Bridges (chat <-> Responses, agentic loop follow-ups) forward litellm_params as
`completion()` kwargs. Any key the param builder does not recognize is swept into
extra_body, and OpenAI rejects the call with `Unknown parameter: 'model_alias_map'`.
"""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Disallowed test comment This explanatory docstring violates the repository directive limiting comments to complex logic, tool input, or TODOs, so it must be removed before merging

Suggested change
"""Bridges (chat <-> Responses, agentic loop follow-ups) forward litellm_params as
`completion()` kwargs. Any key the param builder does not recognize is swept into
extra_body, and OpenAI rejects the call with `Unknown parameter: 'model_alias_map'`.
"""

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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.

3 participants