Skip to content

fix(reasoning): suppress reasoning.summary for gpt-5.3-codex-spark - #65

Open
S-kkipie wants to merge 1 commit into
bman654:mainfrom
S-kkipie:fix/codex-spark-reasoning-summary
Open

fix(reasoning): suppress reasoning.summary for gpt-5.3-codex-spark#65
S-kkipie wants to merge 1 commit into
bman654:mainfrom
S-kkipie:fix/codex-spark-reasoning-summary

Conversation

@S-kkipie

Copy link
Copy Markdown

Fixes #64.

The bug

gpt-5.3-codex-spark returns an empty response — HTTP 200, no content blocks, output_tokens: 0 — for any request carrying a reasoning effort. In Claude Code this surfaces as No assistant messages found, and because Claude Code sends output_config.effort on every request when effortLevel is set in ~/.claude/settings.json, spark is unusable for those users rather than degraded.

Root cause

Not the effort value itself. The upstream 400 is:

Unsupported parameter: 'reasoning.summary' is not supported with the
'gpt-5.3-codex-spark' model.
  type: invalid_request_error
  code: unsupported_parameter
  param: reasoning.summary

@ai-sdk/openai derives the summary from the effort (dist/index.js):

const resolvedReasoningSummary =
  openaiOptions?.reasoningSummary !== undefined
    ? openaiOptions.reasoningSummary
    : resolvedReasoningEffort != null && resolvedReasoningEffort !== 'none'
      ? 'detailed'
      : undefined;

So effortProviderOptions returning { openai: { reasoningEffort } } implies reasoning.summary: 'detailed' on the wire, and spark rejects the whole request. That explains the otherwise odd effort matrix — low/medium/high/xhigh fail while none/max work. The passing values are exactly the ones mapCodexEffortToOpenAI maps to undefined, which drops the reasoning object entirely and takes summary with it. Nothing about effort itself is unsupported.

The fix

Pin reasoningSummary: null for codex-spark. The AI SDK only omits the field when it is explicitly set (undefined triggers the derivation), so null suppresses summary while leaving effort intact.

Scoped to /codex-spark(?:-|$)/i. Other Codex models — gpt-5.1-codex-max, gpt-5-codex — accept summaries and are untouched.

I considered wiring this through the existing supportsSummaries capability flag instead, but that field is currently descriptive metadata only — nothing reads it on the request path — so routing a wire-format fix through it would have meant changing what the flag means. This follows the established per-model quirk-helper pattern next to isGpt56Model instead.

Verification

Against the real openai-oauth route (endpoint mode, ChatGPT/Codex plan):

model low medium high xhigh max none
gpt-5.3-codex-spark (before)
gpt-5.3-codex-spark (after)
gpt-5.6-sol (after)
gpt-5.4 (after)

Output scales with effort as expected after the fix (medium → 6 output tokens, xhigh → 76 on the same one-line prompt), so effort is genuinely reaching the model rather than being silently dropped.

Tests: 4 parametrized cases pinning the suppression across effort levels, plus one asserting other Codex models keep their summary. Both directions fail if the fix is reverted — verified by reverting it (4 failures) and restoring.

pnpm typecheck && pnpm test && pnpm build all pass locally (1183 tests).

Note on the empty-200 behavior

Separate from this fix, and left alone here: when the OAuth WebSocket receives type: "error" with invalid_request_error, handleResponseEvent records a diagnostic and closes the context, but the request still completes as a 200 with an empty message. That turned a precise unsupported_parameter into a silent empty response, and diagnosing it required --ws-diagnostics plus a patched build to recover the message text — the diagnostic path hashes errorMessage rather than logging it. Surfacing upstream invalid_request_error as an error response would make this class of failure much cheaper to debug, but it touches the WebSocket transport and belongs in its own PR. Happy to open one if you'd like it.

gpt-5.3-codex-spark rejects `reasoning.summary` with a 400:

  Unsupported parameter: 'reasoning.summary' is not supported with the
  'gpt-5.3-codex-spark' model.  (code: unsupported_parameter)

The AI SDK derives `summary: 'detailed'` from any reasoning effort other
than 'none' unless `reasoningSummary` is set explicitly, so every effort
level that maps to a wire value made the request fail. The WebSocket
transport surfaces that upstream error as an empty 200 response, which
reaches Claude Code as "No assistant messages found" — spark was
unusable for anyone with `effortLevel` set in settings.json.

Pass `reasoningSummary: null` for codex-spark so the field is omitted
while effort control is preserved.

Verified against the real openai-oauth route: spark now returns content
at low/medium/high/xhigh, with output scaling by effort. gpt-5.6-sol and
gpt-5.4 are unchanged.
@bman654

bman654 commented Jul 29, 2026

Copy link
Copy Markdown
Owner

thanks for the submission. Will review shortly. Since you have a repro for the error handling bug, I'd definitely appreciate a PR if you've got time.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gpt-5.3-codex-spark returns empty responses when effort is set (unsupported reasoning parameter)

2 participants