fix(openai): accept missing content-type on ChatGPT Responses SSE stream#90399
fix(openai): accept missing content-type on ChatGPT Responses SSE stream#90399baanish wants to merge 3 commits into
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 4, 2026, 1:32 PM ET / 17:32 UTC. Summary 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 follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Next step before merge
Security Review detailsBest 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 changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +24, Tests +136. Total +160 across 5 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
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 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. |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
@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 |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
154f5eb to
f92246b
Compare
|
@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. |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
@clawsweeper automerge |
|
🦞🔧
Draft PRs stay fix-only until GitHub marks them ready for review. Pause with Automerge progress:
|
|
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.
fish notes: model gpt-5.5, reasoning high; reviewed against f1924ad. |
Summary
Fixes #90382.
ChatGPT-OAuth
openai-chatgpt-responsesstreams can return HTTP 200 with a valid SSE body but nocontent-typeheader fromhttps://chatgpt.com/backend-api/codex/responses. The existing stream guard rejected that successful stream asinvalid_provider_content_type, soopenai/gpt-5.5failed before the SDK could consume the SSE events.This PR now does two things:
Accept: text/event-streamrequest option for native ChatGPT Responses SDK streaming callsassertOpenAISdkStreamContentType: allow an empty/missingcontent-typeonly when the model API isopenai-chatgpt-responses, the response is OK, and a body existsWrong content types such as
text/htmlstill 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 nocontent-typeheader. The response path then hit the guard insrc/agents/provider-transport-fetch.tsand threw solely because the header was missing.The earlier Accept-header-only patch was not enough: with
Accept: text/event-streamcompiled 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:assertOpenAISdkStreamContentTypesrc/agents/provider-transport-fetch.ts:248: missing content type is allowed only forapi === "openai-chatgpt-responses"src/agents/openai-transport-stream.ts:1846: SDK request options helpersrc/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:content-type+api=openai-chatgpt-responses+ 2xx + body is acceptedtext/htmlon the same API is still rejectedcontent-typeonapi=openai-responsesis still rejectedExisting OpenAI transport coverage still asserts the defensive
Accept: text/event-streamoption 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.5usingapi=openai-chatgpt-responsesfailed when the livechatgpt.com/backend-api/codex/responsesstream returned HTTP 200 with valid SSE bytes but an empty/missingcontent-typeheader. The after-fix behavior should accept that stream instead of throwingProviderHttpError 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.tsagainstopenai/gpt-5.5through 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.5and 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:
Observed result after fix: The same OAuth setup that failed with HTTP 200 + missing
content-typenow accepts the ChatGPT Responses SSE stream. The directopenai/gpt-5.5probe returnsok, and the default model chain resolves towinnerProvider=openai/winnerModel=gpt-5.5instead 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:
Results:
pnpm build:strict-smokeexited 0node scripts/check-deprecated-jsdoc.mjsexited 0 locally after the CI topology failure reportOne broader local lint command did not complete cleanly:
It failed in an unrelated
qa-channelboundary DTS path (extensions/qa-channel/src/bus-client.tsmissingQaBus*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.