What happened
Any one-shot completion through the OpenAI Codex provider fails with HTTP 400 Stream must be set to true.
OpenAiCodexHandler.completePrompt() posts its own request body with stream: false, but the Codex subscription endpoint (/backend-api/codex/responses) only accepts streaming requests and rejects the call outright.
This affects every caller that goes through singleCompletionHandler — commit-message generation, prompt enhancement and context condensing — while normal chat is unaffected, since createMessage streams and hard-codes stream: true.
Reported by @taltas in #1227 (comment), with a screenshot of the 400.
Steps to reproduce
- Sign in with an OpenAI Codex (ChatGPT subscription) profile.
- Trigger anything that uses a single completion rather than a chat stream — e.g. Enhance Prompt.
- The request fails with
400 ... Stream must be set to true.
Expected behavior
A one-shot completion returns text, like every other provider.
Additional context
Two related problems in the same area:
- The provider spec asserted
expect(body.stream).toBe(false), so the test suite pinned the bug in place rather than catching it.
executeRequest always creates its own AbortController and never links metadata?.abortSignal, so cancellation never reaches the wire on this provider.
The fix is to run the existing streaming transport and join the text chunks, which also inherits the OAuth refresh-and-retry and the SDK→SSE fallback instead of duplicating them.
What happened
Any one-shot completion through the OpenAI Codex provider fails with HTTP 400
Stream must be set to true.OpenAiCodexHandler.completePrompt()posts its own request body withstream: false, but the Codex subscription endpoint (/backend-api/codex/responses) only accepts streaming requests and rejects the call outright.This affects every caller that goes through
singleCompletionHandler— commit-message generation, prompt enhancement and context condensing — while normal chat is unaffected, sincecreateMessagestreams and hard-codesstream: true.Reported by @taltas in #1227 (comment), with a screenshot of the 400.
Steps to reproduce
400 ... Stream must be set to true.Expected behavior
A one-shot completion returns text, like every other provider.
Additional context
Two related problems in the same area:
expect(body.stream).toBe(false), so the test suite pinned the bug in place rather than catching it.executeRequestalways creates its ownAbortControllerand never linksmetadata?.abortSignal, so cancellation never reaches the wire on this provider.The fix is to run the existing streaming transport and join the text chunks, which also inherits the OAuth refresh-and-retry and the SDK→SSE fallback instead of duplicating them.