Commit 9fbd47b
fix(proxy): strip Codex lite header on the HTTP /responses path (headroomlabs-ai#1663)
## Description
The WebSocket `/responses` handler already drops
`X-OpenAI-Internal-Codex-Responses-Lite` before forwarding upstream
(headroomlabs-ai#1543) — OpenAI rejects newer Codex models (gpt-5.5 / gpt-5.4 /
gpt-5.4-mini) when this client-only header leaks. The **HTTP POST
`/responses`** handler (`handle_openai_responses`), however, forwards
request headers verbatim after `_strip_internal_headers` (which removes
only `x-headroom-*`), so on the HTTP path the lite header still reaches
`chatgpt.com/backend-api/codex/responses`. This closes that remaining
un-stripped path so both `/responses` transports behave identically.
Closes # <!-- no tracking issue; found during a live support
investigation -->
## Type of Change
- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)
## Changes Made
- `headroom/proxy/handlers/openai.py`: in `handle_openai_responses`
(HTTP POST path), immediately after `headers =
_strip_internal_headers(headers)`, drop any header whose lowercased name
equals `_CODEX_RESPONSES_LITE_HEADER` — mirroring the existing
WS-handler filter. No new imports (the constant is module-level); the WS
path is unchanged.
- `tests/test_openai_codex_routing.py`: add
`test_handle_openai_responses_strips_codex_lite_header_upstream`, which
pushes the lite header plus an adjacent header through the HTTP POST
handler and asserts the lite header is dropped upstream while the
adjacent header survives.
## Testing
- [x] Unit tests pass (`pytest`) — directly-relevant files (see output)
- [x] Linting passes (`ruff check .`)
- [x] Type checking passes (`mypy headroom`)
- [x] New tests added for new functionality
- [ ] Manual testing performed — no live upstream traffic (see Real
Behavior Proof)
### Test Output
```text
$ uv run --extra dev pytest tests/test_openai_codex_routing.py tests/test_openai_codex_ws_lifecycle.py -q
39 passed in 1.13s
$ uv run ruff check .
All checks passed!
$ uv run --extra dev mypy headroom
Success: no issues found in 404 source files
```
## Real Behavior Proof
- Environment: local `uv` venv (Python 3.10), no live provider required.
- Exact command / steps: `uv run --extra dev pytest
tests/test_openai_codex_routing.py::test_handle_openai_responses_strips_codex_lite_header_upstream
tests/test_openai_codex_ws_lifecycle.py::test_ws_codex_responses_lite_header_is_not_forwarded_upstream`
- Observed result: the new test drives a ChatGPT-auth HTTP POST
`/responses` request carrying `X-OpenAI-Internal-Codex-Responses-Lite:
true` and an adjacent `X-OpenAI-Debug: keep-me`; the captured upstream
headers contain the adjacent header but not the lite header. The WS
regression test still passes.
- Not tested: live Codex traffic against OpenAI with real credentials.
(Separately: for a WebSocket-only ChatGPT-auth client the lite signal is
not carried as an HTTP header on the handshake — that case is out of
scope here.)
## Review Readiness
- [x] I have performed a self-review
- [x] This PR is ready for human review
## Checklist
- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation — N/A (no
doc-facing behavior change)
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective
- [x] New and existing unit tests pass locally with my changes
- [ ] I have updated the CHANGELOG.md if applicable — N/A (changelog is
generated from conventional commits; commit is `fix(proxy): …`)
## Screenshots (if applicable)
N/A — backend header-handling change.
## Additional Notes
- Scope of checks: `pytest` was run on the two directly-relevant files
(`test_openai_codex_routing.py`, `test_openai_codex_ws_lifecycle.py`),
not the entire suite; `ruff check .` and `mypy headroom` were run
repo-/package-wide.
- Complements headroomlabs-ai#1543 (WS path) by closing the HTTP POST path; it is the
minimal mirror of that filter.
- `Closes #` intentionally blank: found during a support investigation
with no tracking issue.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: JD Davis <mxjerrett@gmail.com>1 parent 646e705 commit 9fbd47b
2 files changed
Lines changed: 41 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3071 | 3071 | | |
3072 | 3072 | | |
3073 | 3073 | | |
| 3074 | + | |
| 3075 | + | |
| 3076 | + | |
| 3077 | + | |
| 3078 | + | |
| 3079 | + | |
| 3080 | + | |
| 3081 | + | |
3074 | 3082 | | |
3075 | 3083 | | |
3076 | 3084 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
288 | 288 | | |
289 | 289 | | |
290 | 290 | | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
291 | 324 | | |
292 | 325 | | |
293 | 326 | | |
| |||
0 commit comments