[Bug] Silent TUI crash when parent thread and sub-agent concurrently poll long-running process via write_stdin

Open 💬 2 comments Opened Apr 1, 2026 by raederhans

What version of Codex CLI is running?

0.118.0

What subscription do you have?

GPT Pro

Which model were you using?

GPT 5.4

What platform is your computer?

Windows 11 Pro 10.0.26200

What terminal emulator and version are you using (if applicable)?

CLI powershell

What issue are you seeing?

When a parent session spawns a sub-agent (via spawn_agent) and both sessions concurrently enter a write_stdin polling loop waiting for a long-running process to finish, the Codex TUI crashes silently — no error message, no warning, no graceful shutdown. Both WebSocket connections close simultaneously and the process dies, leaving thread state corrupted in state_5.sqlite.

What steps can reproduce the bug?

Start a Codex session and give it a task that involves running a long-running command (e.g. python -m unittest discover)

The main session calls exec_command on the long-running process — this yields a streaming session ID

The main session also calls spawn_agent with fork_context: true to create a parallel sub-agent for the same task

Both the parent thread and the sub-agent independently call write_stdin in a polling loop (e.g. every 1–5 seconds) to check if the process has finished

After a few polling cycles, both WebSocket connections drop within ~1 second of each other

The TUI process dies silently — no error is shown to the user

When Codex is reopened, thread_spawn_edges in state_5.sqlite contains entries stuck at status='open' for both threads

What is the expected behavior?

Either: the TUI gracefully handles the concurrent WebSocket error and shows an error message

Or: sub-agents are rate-limited / serialized so that two sessions cannot simultaneously poll the same or overlapping processes

At minimum: the crash should be logged and the thread state should be closed properly in the database

Additional information

TUI process crashes silently (no error message to user, no panic log entry in codex-tui.log)

A 28-second gap appears in codex-tui.log — no log entries at all during that window — confirming the process died rather than hanging

Both WebSocket client connections close within ~1s of each other:
2026-04-01T00:56:31.321Z INFO ...thread_id=019d4688...: model_client.stream_responses_websocket: close
2026-04-01T00:56:32.797Z INFO ...thread_id=019d468a...: model_client.stream_responses_websocket: close
2026-04-01T00:56:32 → 00:57:01 [28-second gap — no log entries]
2026-04-01T00:57:01.249Z INFO list_models: fresh TUI initialization

thread_spawn_edges left with 16 rows at status='open' after the crash

threads table left with entries at archived=0 despite sessions being dead

The write_stdin polling pattern creates a tight loop of API calls: each poll that returns "process still running" causes the model to immediately schedule another write_stdin call. With two concurrent sessions doing this, the combined API call rate doubles. The TUI appears to be unable to handle the event stream from two concurrent GPT-5.4 WebSocket connections under this load pattern.

Manually closing the zombie thread_spawn_edges entries and running VACUUM on state_5.sqlite seems to restores normal behavior, yet I am not sure when this can happen again.

There should be a rule set that: Avoid having the parent thread and a sub-agent run exec_command on long-running processes at the same time. Only one session should enter the write_stdin polling loop at any given time.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗