Skip to content

fix(agent): return failed turns when Codex app-server exits during RPC writes - #83129

Open
fangliquanflq wants to merge 2 commits into
NousResearch:mainfrom
fangliquanflq:fix/codex-app-server-write-races
Open

fix(agent): return failed turns when Codex app-server exits during RPC writes#83129
fangliquanflq wants to merge 2 commits into
NousResearch:mainfrom
fangliquanflq:fix/codex-app-server-write-races

Conversation

@fangliquanflq

Copy link
Copy Markdown
Contributor

What does this PR do?

Codex turns and native compaction now return structured failures when the app-server exits during an RPC write instead of aborting the enclosing conversation with a runtime exception. The failed session is marked for retirement, allowing a later turn to start a clean child, while unrelated runtime errors remain visible.

Symptom

If the Codex child exits after startup succeeds but before turn/start, thread/compact/start, or an approval response reaches stdin, the write exception can escape run_turn() or compact_thread() instead of returning a failed TurnResult.

Impact

Affected Codex users can lose the current conversation or compaction call outside the normal structured error path. A dead session may also remain eligible for reuse instead of being retired. Frequency and broader blast radius were not measured.

Bug Cause

Trigger: agent/transports/codex_app_server.py in CodexAppServerClient._send() when stdin is closed or a write raises BrokenPipeError or ValueError.

Causal chain:

  1. The Codex child passes the session startup or liveness check.
  2. The child exits before the following JSON-RPC write completes.
  3. The client raises a plain runtime exception, but the session catches only RPC-domain errors and timeouts, so no structured retiring result is produced.

Why it is wrong: A process can exit between any liveness check and the next write. Transport loss is an expected lifecycle race, but plain RuntimeError cannot be handled narrowly without also hiding unrelated programming failures.

Working sibling / contrast: RPC-domain errors and timeouts already become structured session results. Steering and interruption already treat expected control-path failures as non-fatal.

Ruled out: An extra is_alive() check cannot remove the check-to-write race. Catching all runtime exceptions would mask unrelated defects.

Fix

  • Add CodexAppServerTransportError for closed or broken child stdin writes.
  • Remove pending JSON-RPC requests when their initial send fails.
  • Convert initial turn, compaction, and approval-response transport loss into redacted failed results with should_retire=True.
  • Treat the same transport loss during steering and interruption as non-fatal without suppressing unrelated runtime exceptions.

Related Issue

Fixes #83127

Type of Change

  • Bug fix

Changes Made

  • agent/transports/codex_app_server.py - define the transport write exception and clean pending request state after failed sends.
  • agent/transports/codex_app_server_session.py - handle transport loss at turn, compaction, approval-response, steering, and interrupt boundaries.
  • tests/agent/transports/test_codex_app_server_runtime.py - cover typed closed-stdin and pending cleanup behavior.
  • tests/agent/transports/test_codex_app_server_session.py - cover structured retirement, redaction, sibling control paths, and unrelated runtime errors.

How to Test

  1. Reproduce a child exit after startup but before the initial turn or compaction request write, then confirm a failed retiring result is returned.
  2. Exercise approval-response, steering, interrupt, pending cleanup, redaction, and unrelated runtime error contracts.
  3. Run the focused automated suite:
scripts/run_tests.sh tests/agent/transports/test_codex_app_server_runtime.py tests/agent/transports/test_codex_app_server_session.py

Result: 65 passed.

Checklist

  • My commit messages follow Conventional Commits.
  • I searched for existing PRs to make sure this is not a duplicate.
  • My PR contains only changes related to this fix.
  • I ran the repository test entry point on the relevant tests and all tests pass.
  • I added behavior tests for the bug fix.
  • I verified the transport contracts on Windows 10.

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/openai OpenAI / Codex Responses API P3 Low — cosmetic, nice to have sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have provider/openai OpenAI / Codex Responses API sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Codex turns can abort when the app-server exits during RPC writes

2 participants