fix(codex): preserve terminal WebSocket API errors - #693
Closed
yxlyx wants to merge 2 commits into
Closed
Conversation
Codex closes its Responses socket immediately after a type:error frame. Graff previously waited for another frame, discarded the accumulated API body on EOF, and retried an unchanged deterministic request across fresh WS and SSE transports. Classify authoritative error frames as terminal API responses, retire the closing socket without charging the transport ladder, and allow only recognized stale chains to rebuild once with full input. Bound parsed code/message diagnostics before tracing or displaying them, including through the fullscreen TUI. Add loopback, interactive PTY, stale-chain, diagnostic-redaction, and headless TUI regressions while retaining the existing fallback and compaction trajectories. Co-Authored-By: Codegraff <blackfloofie@codegraff.com>
The local Zig 0.16 compiler accepted a repeated string expression in the new diagnostic test, but the repository's pinned Zig 0.17 CI compiler rejects it during parsing on both Linux and Windows. Build the same long message with the repository's established @Splat pattern so the test remains portable across both toolchains. Co-Authored-By: Codegraff <blackfloofie@codegraff.com>
Owner
|
Folded into release/v0.0.282 (#696). Closing as superseded. |
yermakoffivan
pushed a commit
to yermakoffivan/codegraff
that referenced
this pull request
Aug 31, 2026
…API errors yxlyx: treat Responses type:error as a terminal API response, keep a bounded last_api_error, and rebuild full input once on a stale chain.
yermakoffivan
pushed a commit
to yermakoffivan/codegraff
that referenced
this pull request
Aug 31, 2026
…justrach#694 Record the three product merges on the new release branch, remap the TLS-generation ADR to 0048 so 0042 stays TUI claims, and leave justrach#277 / justrach#200 parked.
yermakoffivan
pushed a commit
to yermakoffivan/codegraff
that referenced
this pull request
Aug 31, 2026
justrach#693 pushed the request loop to 602 lines. Move keep-alive retry and scratch-arena reset into agent_request_scratch.zig and ratchet the suite floor to 1817 for the yxlyx paste / WS / TLS tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
type:errorframe as a terminal API response, retain its accumulated SSE-shaped body, and mark the server-closing socket dead instead of waiting for EOF.last_api_errorand the default trace without copying the raw frame envelope, request, headers, or auth-adjacent fields.previous_response_idchain; deterministic generic API errors return immediately without consuming the WS retry/SSE fallback ladder.Why
Problem / failure mode
Codex sends a useful
type:errorframe and then closes the WebSocket. Graff accumulated the frame but recognized only two special error codes as terminal. For a generic error it waited for another frame, interpreted the expected close as transport loss, discarded the API body, redialed a fresh WS, and finally replayed the unchanged deterministic request over SSE. Users saw onlyBad Request, the trace lost the actual code/message, and the session unnecessarily latched off WS. The fullscreen TUI then replaced API failures with an API-key troubleshooting row.Reason for this approach
The error frame is an authoritative provider response, so it belongs on the existing Responses parser/API-error path rather than the transport ladder. That keeps one shared parser for WS and SSE, preserves structured recovery such as stale-chain detection, and gives diagnostics one explicit privacy/size boundary before they reach traces or UI. Capturing whether a chain existed before retiring the dead socket allows a safe one-shot full-input rebuild without retrying generic 400 bodies.
Constraints and trade-offs
errorcandidate check; this adds allocation only on rare candidate frames and avoids brittle substring classification when JSON contains whitespace or quoted prose.TUI/sim.zigpath; the provider/socket half is exercised independently through the real interactive PTY runtime and loopback backend.Rejected alternatives
continuewas rejected because it changes conversation semantics; a recognized stale chain can be recovered safely by rebuilding the same full input instead.Verification
scripts/eval-tier1.sh— green: format, line ceiling, spec, reachability, build, 1,760 tests passed (1 skipped), 460/460 TUI tests, 17/17 TUI guard probes, invariants, SDK.python3 scripts/test-pty-codex-ws.py ./zig-out/bin/graff— all eight healthy/retry/fallback/error/chain/compaction trajectories passed.Fixes #692