Skip to content

fix(openai): accept missing content-type on ChatGPT Responses SSE stream#90399

Closed
baanish wants to merge 3 commits into
openclaw:mainfrom
baanish:codex/fix-chatgpt-responses-accept-sse-header
Closed

fix(openai): accept missing content-type on ChatGPT Responses SSE stream#90399
baanish wants to merge 3 commits into
openclaw:mainfrom
baanish:codex/fix-chatgpt-responses-accept-sse-header

Conversation

@baanish

@baanish baanish commented Jun 4, 2026

Copy link
Copy Markdown

Summary

Fixes #90382.

ChatGPT-OAuth openai-chatgpt-responses streams can return HTTP 200 with a valid SSE body but no content-type header from https://chatgpt.com/backend-api/codex/responses. The existing stream guard rejected that successful stream as invalid_provider_content_type, so openai/gpt-5.5 failed before the SDK could consume the SSE events.

This PR now does two things:

  • keeps the defensive Accept: text/event-stream request option for native ChatGPT Responses SDK streaming calls
  • makes the real fix in assertOpenAISdkStreamContentType: allow an empty/missing content-type only when the model API is openai-chatgpt-responses, the response is OK, and a body exists

Wrong content types such as text/html still fail, and missing content type on other OpenAI-compatible stream APIs still fails.

Root cause

Captured live behavior showed the server returning a valid SSE body such as event: response.created / response.output_text.done, but with no content-type header. The response path then hit the guard in src/agents/provider-transport-fetch.ts and threw solely because the header was missing.

The earlier Accept-header-only patch was not enough: with Accept: text/event-stream compiled in, the live endpoint still returned HTTP 200 with no content type. The Accept override remains useful hygiene, but the guard tolerance is the fix that handles the captured server behavior.

Relevant source points:

  • src/agents/provider-transport-fetch.ts:236: assertOpenAISdkStreamContentType
  • src/agents/provider-transport-fetch.ts:248: missing content type is allowed only for api === "openai-chatgpt-responses"
  • src/agents/openai-transport-stream.ts:1846: SDK request options helper
  • src/agents/openai-transport-stream.ts:1947: streaming Responses dispatch passes { stream: true }

Tests

Added provider transport regression coverage in src/agents/provider-transport-fetch.test.ts:

  • missing content-type + api=openai-chatgpt-responses + 2xx + body is accepted
  • text/html on the same API is still rejected
  • missing content-type on api=openai-responses is still rejected

Existing OpenAI transport coverage still asserts the defensive Accept: text/event-stream option is scoped to native ChatGPT Responses stream requests and not added to normal/default OpenAI request options.

Real behavior proof

  • Behavior or issue addressed: ChatGPT-OAuth openai/gpt-5.5 using api=openai-chatgpt-responses failed when the live chatgpt.com/backend-api/codex/responses stream returned HTTP 200 with valid SSE bytes but an empty/missing content-type header. The after-fix behavior should accept that stream instead of throwing ProviderHttpError code=invalid_provider_content_type.

  • Real environment tested: Reporter's local OpenClaw checkout with OAuth auth live, using the compiled guard-tolerance patch in src/agents/provider-transport-fetch.ts against openai/gpt-5.5 through ChatGPT OAuth.

  • Exact steps or command run after this patch: Rebuilt/ran the local OpenClaw checkout containing the guard-tolerance patch, then ran the provider probe for openai/gpt-5.5 and a default-chain model resolution run using the same OAuth setup that reproduced [Bug]: ChatGPT-OAuth gpt-5.5 fails with invalid_provider_content_type after 2026.6.1 (SDK Responses stream missing Accept: text/event-stream) #90382.

  • Evidence after fix: Copied live terminal output from the reporter's local OpenClaw setup:

openai/gpt-5.5 ... ok
default chain resolved winnerProvider=openai winnerModel=gpt-5.5
  • Observed result after fix: The same OAuth setup that failed with HTTP 200 + missing content-type now accepts the ChatGPT Responses SSE stream. The direct openai/gpt-5.5 probe returns ok, and the default model chain resolves to winnerProvider=openai / winnerModel=gpt-5.5 instead of falling through to fallback providers.

  • What was not tested: I did not personally run a live ChatGPT-OAuth endpoint call from this CI/local author environment because that requires the reporter's OAuth account. The reporter-provided after-fix live output above is the real behavior proof; local author validation covers unit tests, typecheck, formatting, lint, and build smoke.

Local validation

Commands run locally:

node scripts/test-projects.mjs src/agents/provider-transport-fetch.test.ts -- -t "ChatGPT Responses streams|non-ChatGPT Responses streams"
node scripts/test-projects.mjs src/agents/provider-transport-fetch.test.ts
node scripts/test-projects.mjs src/agents/openai-transport-stream.test.ts -- -t "adds SSE Accept only to native ChatGPT Responses stream requests"
node scripts/test-projects.mjs src/agents/openai-transport-stream.test.ts
pnpm exec oxfmt --check src/agents/provider-transport-fetch.ts src/agents/provider-transport-fetch.test.ts src/agents/openai-transport-stream.ts src/agents/openai-transport-stream.test.ts
pnpm exec oxlint src/agents/provider-transport-fetch.ts src/agents/provider-transport-fetch.test.ts src/agents/openai-transport-stream.ts src/agents/openai-transport-stream.test.ts
node scripts/run-tsgo.mjs -p tsconfig.core.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/core.tsbuildinfo
node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.core.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/core-test.tsbuildinfo
pnpm build:strict-smoke
node scripts/check-deprecated-jsdoc.mjs

Results:

  • focused provider transport tests: 3 passed
  • full provider transport file: 71 passed
  • focused OpenAI transport Accept regression: 1 passed
  • full OpenAI transport file: 241 passed
  • file-scoped format and lint checks passed
  • core typecheck and core test typecheck exited 0
  • pnpm build:strict-smoke exited 0
  • node scripts/check-deprecated-jsdoc.mjs exited 0 locally after the CI topology failure report

One broader local lint command did not complete cleanly:

pnpm lint:core

It failed in an unrelated qa-channel boundary DTS path (extensions/qa-channel/src/bus-client.ts missing QaBus* exports from ./protocol.js / openclaw/plugin-sdk/qa-channel-protocol). That failure is outside this PR's changed files.

Risk

Highest risk: accepting a real error envelope with a missing/misleading content type.

Mitigation: the tolerance is scoped to empty content type only, on successful responses with a body, and only for api=openai-chatgpt-responses. HTML and other wrong content types still go through the existing rejection path, and missing content type for other OpenAI-compatible APIs remains rejected.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS proof: supplied External PR includes structured after-fix real behavior proof. labels Jun 4, 2026
@clawsweeper

clawsweeper Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 4, 2026, 1:32 PM ET / 17:32 UTC.

Summary
The PR adds a ChatGPT Responses stream Accept header, tolerates missing content-type only for successful openai-chatgpt-responses SSE bodies, and adds focused regression tests plus a deprecation annotation for a migration alias.

PR surface: Source +24, Tests +136. Total +160 across 5 files.

Reproducibility: yes. from source and issue logs: a stream=true ChatGPT Responses request with HTTP 200, a body, and missing content-type reaches the current guard and throws invalid_provider_content_type; I did not run a live OAuth probe locally.

Review metrics: none identified.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🦞 diamond lobster
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Next step before merge

  • [P2] No repair lane is needed because the branch already contains the narrow code and regression-test fix plus sufficient real behavior proof.

Security
Cleared: The diff narrows a transport guard exception to successful ChatGPT Responses streams with empty content-type and does not change dependencies, workflows, secrets, or package execution surfaces.

Review details

Best possible solution:

Land the narrow provider transport fix with its focused positive and negative regression coverage, keeping the strict content-type guard for wrong content types and other APIs.

Do we have a high-confidence way to reproduce the issue?

Yes from source and issue logs: a stream=true ChatGPT Responses request with HTTP 200, a body, and missing content-type reaches the current guard and throws invalid_provider_content_type; I did not run a live OAuth probe locally.

Is this the best way to solve the issue?

Yes; the fix is at OpenClaw's stricter-than-SDK stream guard and is scoped to the observed ChatGPT Responses missing-header case, while the Accept header matches Codex/OpenAI streaming expectations and is not relied on as the only fix.

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against c87c1569d58f.

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix live terminal output from the same reporter OAuth setup showing the OpenAI probe and default model resolution succeeding after the patch.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🦞 diamond lobster.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes after-fix live terminal output from the same reporter OAuth setup showing the OpenAI probe and default model resolution succeeding after the patch.
  • remove rating: 🦪 silver shellfish: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.
  • remove status: 📣 needs proof: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P1: The PR fixes a reported 2026.6.1 regression that makes ChatGPT OAuth OpenAI streams fail before model output for affected users.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes after-fix live terminal output from the same reporter OAuth setup showing the OpenAI probe and default model resolution succeeding after the patch.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix live terminal output from the same reporter OAuth setup showing the OpenAI probe and default model resolution succeeding after the patch.
Evidence reviewed

PR surface:

Source +24, Tests +136. Total +160 across 5 files.

View PR surface stats
Area Files Added Removed Net
Source 3 29 5 +24
Tests 2 136 0 +136
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 5 165 5 +160

What I checked:

Likely related people:

  • steipete: Current-main blame for both the strict content-type guard and the Responses SDK request-options path points to Peter Steinberger's commit 2016d32; shallow/grafted history limits deeper provenance. (role: recent area contributor; confidence: medium; commits: 2016d3218794; files: src/agents/provider-transport-fetch.ts, src/agents/openai-transport-stream.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added the rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. label Jun 4, 2026
@baanish

baanish commented Jun 4, 2026

Copy link
Copy Markdown
Author

@clawsweeper re-review

The PR body now includes the real behavior proof section, and the prior Codex review appears to have timed out before evaluating the patch.

@clawsweeper

clawsweeper Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P1 High-priority user-facing bug, regression, or broken workflow. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. labels Jun 4, 2026
@baanish baanish changed the title fix(openai): send Accept: text/event-stream on ChatGPT Responses fix(openai): accept missing content-type on ChatGPT Responses SSE stream Jun 4, 2026
@baanish

baanish commented Jun 4, 2026

Copy link
Copy Markdown
Author

@clawsweeper re-review

The PR has been updated with the corrected root cause and fix: the live ChatGPT Responses stream returns valid SSE with a missing content-type, so the guard now tolerates missing content-type only for successful openai-chatgpt-responses streams. The Accept-header change remains as defensive hygiene, but the PR body now calls out that Accept alone did not fix #90382.

@clawsweeper

clawsweeper Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@openclaw-barnacle openclaw-barnacle Bot added triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. and removed proof: supplied External PR includes structured after-fix real behavior proof. labels Jun 4, 2026
@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 4, 2026
@baanish baanish force-pushed the codex/fix-chatgpt-responses-accept-sse-header branch from 154f5eb to f92246b Compare June 4, 2026 17:16
@openclaw-barnacle openclaw-barnacle Bot added the commands Command implementations label Jun 4, 2026
@baanish

baanish commented Jun 4, 2026

Copy link
Copy Markdown
Author

@clawsweeper re-review

New head is f92246b. The real-behavior-proof body now passes the repo proof parser, and the topology/architecture failure is fixed on this branch.

@clawsweeper

clawsweeper Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jun 4, 2026
@clawsweeper clawsweeper Bot added status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 4, 2026
@Takhoffman

Copy link
Copy Markdown
Contributor

@clawsweeper automerge

@clawsweeper

clawsweeper Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

🦞🔧
ClawSweeper automerge is enabled.

Draft PRs stay fix-only until GitHub marks them ready for review. Pause with /clawsweeper stop.

Automerge progress:

  • 2026-06-05 03:15:52 UTC review queued f92246bddb22 (queued)

@clawsweeper clawsweeper Bot added the clawsweeper:automerge Maintainer opted this PR into bounded ClawSweeper-reviewed automerge label Jun 5, 2026
@clawsweeper

clawsweeper Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper 🐠 reef update

Thanks for the work here. ClawSweeper could not write to the source branch, so it opened a replacement PR rather than letting the fix drift. attribution still points back here.

Why replacement: ClawSweeper could not update the source PR branch directly; GitHub did not grant sufficient push rights to the bot for that branch.
Replacement PR: #90533
Why close: this run explicitly closes the superseded source PR after the credited replacement PR is open, so review continues in one place.
Closing this source PR because this run explicitly enabled source-PR closeout.
The original contribution stays credited in the replacement PR context.
Co-author credit kept:

fish notes: model gpt-5.5, reasoning high; reviewed against f1924ad.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling clawsweeper:automerge Maintainer opted this PR into bounded ClawSweeper-reviewed automerge commands Command implementations P1 High-priority user-facing bug, regression, or broken workflow. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: ChatGPT-OAuth gpt-5.5 fails with invalid_provider_content_type after 2026.6.1 (SDK Responses stream missing Accept: text/event-stream)

2 participants