litellm - ✅(Solved) Fix [Bug]: chagpt pro returns 403 on /v1/chat/completions and 400 on /health [2 pull requests, 1 comments, 1 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#25394Fetched 2026-04-10 03:41:21
View on GitHub
Comments
1
Participants
1
Timeline
5
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×2labeled ×2commented ×1

Fix Action

Fixed

PR fix notes

PR #25371: fix(chatgpt): normalize Responses input and health checks

Description (problem / solution / changelog)

Summary

This PR fixes two ChatGPT Responses regressions that showed up when routing ChatGPT models through LiteLLM.

The first commit makes the ChatGPT Responses provider normalize plain string input values into the message-list shape that ChatGPT expects. The second commit updates the health-check path to send the canonical structured Responses payload instead of relying on provider-specific recovery.

Root Cause

The generic Responses API flow allows string inputs, but ChatGPT's /responses endpoint rejects them with 400 and {"detail":"Input must be a list"}. That surfaced in two places:

  1. Provider calls that reached ChatGPT with a plain string input
  2. UI health checks for mode: responses, which were sending input=prompt or "test"

Changes

  • normalize string ChatGPT Responses input into a single user message with input_text
  • add a ChatGPT provider regression test for string input normalization
  • add a small helper for canonical Responses health-check input
  • update the Responses health handler to use structured input
  • add health-check tests covering the structured Responses path

Impact

  • chatgpt/* models using the Responses API no longer fail when a caller passes a plain string input through LiteLLM
  • ChatGPT model health checks stop returning 400 due to invalid Responses input shape
  • the health-check path now uses the canonical Responses message format instead of relying on provider-specific normalization

Validation

Ran in Docker with Python 3.11 and LiteLLM proxy extras installed:

docker run --rm -v "$PWD:/work" -w /work python:3.11-slim sh -lc \
  'pip install -q --upgrade pip setuptools wheel && \
   pip install -q -e ".[proxy]" pytest pytest-asyncio httpx && \
   pytest tests/test_litellm/llms/chatgpt/responses/test_chatgpt_responses_transformation.py \
          tests/test_litellm/litellm_core_utils/test_health_check_helpers.py -q'

Result: 21 passed

Commit Structure

  • fix(chatgpt): normalize string responses input
  • fix(health): use structured responses input

Changed files

  • litellm/litellm_core_utils/health_check_helpers.py (modified, +19/-1)
  • litellm/llms/chatgpt/responses/transformation.py (modified, +13/-0)
  • tests/test_litellm/litellm_core_utils/test_health_check_helpers.py (modified, +35/-1)
  • tests/test_litellm/llms/chatgpt/responses/test_chatgpt_responses_transformation.py (modified, +18/-0)

PR #25403: fix(chatgpt): preserve responses routing and recover empty output

Description (problem / solution / changelog)

Summary

Fix ChatGPT /v1/chat/completions proxy failures when multiple aliases share the same gpt-5.4 backend model.

Root cause

There were two separate breakpoints in the ChatGPT proxy path:

  1. Router startup re-registered shared backend keys like chatgpt/gpt-5.4 using deployment-level model_info. If one alias set mode: chat, that last write downgraded the shared backend key from responses to chat, which sent /v1/chat/completions to the legacy ChatGPT chat-completions endpoint and surfaced as 403 Forbidden.
  2. Even after routing was corrected, ChatGPT non-stream Responses payloads could still end with response.completed.output=[] while the real assistant message had already been emitted in earlier SSE events. The bridge then raised Unknown items in responses API response: [].

What changed

  • preserve the existing shared backend mode when router deployment registration reuses a provider/model key that already exists in litellm.model_cost
  • teach the ChatGPT Responses parser to recover response.output_item.done entries when response.completed.output is empty
  • add a defensive /responses -> /chat/completions bridge fallback that reconstructs output items from raw SSE when raw_response.output is empty
  • add regression coverage for:
    • shared ChatGPT aliases reusing the same backend model without downgrading responses routing
    • ChatGPT SSE parsing when response.completed.output == []
    • bridge recovery from raw SSE response.output_text.done events

Validation

Ran targeted regression tests in the LiteLLM container runtime:

  • tests/test_litellm/test_router_model_cost_isolation.py -k downgrade
  • tests/test_litellm/llms/chatgpt/responses/test_chatgpt_responses_transformation.py -k recovers_output_items
  • tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_transformation.py -k recovers_empty_output_from_raw_sse

Changed files

  • litellm/completion_extras/litellm_responses_transformation/transformation.py (modified, +157/-20)
  • litellm/llms/chatgpt/responses/transformation.py (modified, +19/-4)
  • litellm/router.py (modified, +12/-0)
  • tests/test_litellm/completion_extras/litellm_responses_transformation/test_completion_extras_litellm_responses_transformation_transformation.py (modified, +234/-0)
  • tests/test_litellm/llms/chatgpt/responses/test_chatgpt_responses_transformation.py (modified, +83/-0)
  • tests/test_litellm/proxy/prompts/test_prompt_endpoints.py (modified, +6/-3)
  • tests/test_litellm/test_router_model_cost_isolation.py (modified, +76/-0)

Code Example

docker run --rm -p 4100:4000 ghcr.io/berriai/litellm:v1.82.3

---

model_list:
    - model_name: chatgpt/gpt-5.4
      litellm_params:
        model: gpt-5.4
        custom_llm_provider: chatgpt
      model_info:
        mode: responses

---

curl -vsS http://<host>:4100/v1/chat/completions \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer <master-key>" \
    -d '{
      "model": "chatgpt/gpt-5.4",
      "messages": [
        {"role": "user", "content": "Reply with exactly: ok"}
      ]
    }'

---
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 using the ChatGPT provider through LiteLLM, I am seeing two related failures:

  1. POST /v1/chat/completions for chatgpt/* returns 403 Forbidden and asks to approve identity.
  2. Health checks for the same models fail with BadRequestError: 400

This appears to be LiteLLM-specific, not an account/IP issue:

  • the same ChatGPT account works from the same IP through Codex CLI
  • the same ChatGPT account works from the same IP through VS Code Kilo / ChatGPT Pro provider

I reproduced this on both:

  • ghcr.io/berriai/litellm:v1.82.3
  • ghcr.io/berriai/litellm:v1.83.3.rc.1

Observed behavior:

  • LiteLLM routes chatgpt/gpt-5.4 to https://chatgpt.com/backend-api/codex/chat/completions
  • that returns a Cloudflare challenge page and surfaces as 403 Forbidden
  • health checks for mode: responses send a plain string input, and the ChatGPT Responses endpoint rejects that with 400 {"detail":"Input must be a list"}

Expected behavior:

  • chatgpt/gpt-5.4 should be routed through the ChatGPT Responses path and work via /v1/chat/completions
  • /health should mark the model healthy instead of failing with BadRequestError: 400

Steps to Reproduce

  1. Start a clean LiteLLM container, for example:
docker run --rm -p 4100:4000 ghcr.io/berriai/litellm:v1.82.3
  1. Configure a ChatGPT model similar to:
  model_list:
    - model_name: chatgpt/gpt-5.4
      litellm_params:
        model: gpt-5.4
        custom_llm_provider: chatgpt
      model_info:
        mode: responses
  1. Call the proxy:
  curl -vsS http://<host>:4100/v1/chat/completions \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer <master-key>" \
    -d '{
      "model": "chatgpt/gpt-5.4",
      "messages": [
        {"role": "user", "content": "Reply with exactly: ok"}
      ]
    }'
  1. Observe that the request returns 403 Forbidden.
  2. Observe that health fails with a 400 / BadRequestError

Note: This may also not happen on fresh instance randomly due to cloudflare algorithms

Relevant log output

What part of LiteLLM is this about?

UI Dashboard

What LiteLLM version are you on ?

v1.82.3

Twitter / LinkedIn details

No response

extent analysis

TL;DR

  • The issue may be resolved by updating the routing configuration for ChatGPT models in LiteLLM to use the correct API endpoint.

Guidance

  • Verify that the chatgpt/gpt-5.4 model is correctly configured to use the ChatGPT Responses API endpoint, rather than the Codex API endpoint.
  • Check the LiteLLM documentation for any specific requirements or guidelines for configuring ChatGPT models.
  • Test the ChatGPT model with a different input format, such as a list, to see if it resolves the BadRequestError: 400 issue.
  • Consider updating to a newer version of LiteLLM, if available, to see if the issue is resolved.

Notes

  • The issue appears to be specific to LiteLLM and may be related to the way it handles ChatGPT models.
  • The fact that the same ChatGPT account works through other interfaces, such as Codex CLI and VS Code Kilo, suggests that the issue is not with the account or IP address.

Recommendation

  • Apply workaround: Update the routing configuration for ChatGPT models in LiteLLM to use the correct API endpoint, as this is the most likely cause of the issue.

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