Repository navigation
Description
What happened
Note
AI-generated issue: drafted by Claude Opus 5.5 (Claude Code via t3 triage) on behalf of @esukram.
The shared Codex app-server went silent three times in ~80 minutes on Oct 31 (17:29, 18:30 and 18:51 UTC), and again on Nov 1 (13:25 UTC) on a newer nightly. Each time:
- it was in the middle of streaming
item/agentMessage/delta; - Codex logged
Failed to write to stdout: Resource temporarily unavailable (os error 11); - it marked its only client connection as disconnected, and dropped every later response and notification while the process stayed alive.
T3 kept sending requests and waited forever. The consequences (stuck workers, a blocked worktree lease) are tracked in #16636. This issue is about the trigger on T3's side.
Diagnosis
Likely cause: read-loop backpressure.
packages/effect-codex-app-server/src/protocol.ts(~L406–430) handles each stdout line before reading the next chunk, and it runsonNotificationhandlers inline.- In
CodexAdapterV2.ts, theitem/started,item/completed,thread/closedandthread/goal/*handlers run underturnTerminalizationPermit.withPermits(1)(~L4528, L4694, L4835, L5157). That is oneSemaphore(1)for all threads on the shared app-server. - So any permit holder that waits stops T3 from reading stdout. Codex fills its non-blocking stdout socket and gets EAGAIN.
Not yet identified: which holder waits. The goal-turn turn/interrupt (~L4511) is ruled out, since it is forked. The terminalization paths (~L5964) are unverified.
Ruled out: event-loop blocking (t3_server_event_loop_delay_max ≤15 ms at the drops), host load (CPU ≤14 %) and cgroup throttling (flat counters, PSI 0).
Fix idea: dispatch handlers off the reader, through a queue or a fork, and/or never hold the permit across an await.
Steps to reproduce
Not yet reduced to a minimal repro.
- A Claude orchestrator repeatedly
delegate_tasks to Codex (reviews), with long streamed replies, on one shared app-server. - Within tens of minutes Codex logs
os error 11on stdout, thendropping message for disconnected connectionfor every message. - Every later request goes unanswered, and running turns get no more events.
Deterministic idea: make a permit holder sleep while another thread streams deltas.
Version
0.1.6-nightly.20261030.3940 (three times on Oct 31) and 0.1.7-nightly.20261031.4004 (Nov 1), both with codex-cli 0.166.0, so it is not specific to one nightly. Triage CLI: 0.1.6.
Environment
- Arch Linux x64, kernel 7.2.11;
t3 serveas a systemd user service; web UI client on the LAN - codex-cli 0.166.0 as the shared app-server; Claude Agent (
claude-opus-5-5) as orchestrator
Evidence
Codex `logs_2.sqlite`, first drop:
2026-10-31 17:29:04 ERROR codex_app_server_transport::transport::stdio Failed to write to stdout: Resource temporarily unavailable (os error 11)
2026-10-31 17:29:04 WARN codex_app_server::transport dropping message for disconnected connection: ConnectionId(0)
On Nov 1 (4004) it was the same: EAGAIN at 13:25:29, last message from Codex at 13:25:28.931, and `thread/start` ids 27 and 32 never answered. Timelines: https://github.com/pingdotgg/t3code/issues/16636#issuecomment-6547877689Related issues
#16636 (consequences), #23854 (bounds control calls, not this trigger), #21321 (worker slots only), #19066, openai/codex#55242
Fix applied or workaround
Stop the wedged app-server: systemctl --user stop t3code-codex-<id>.scope. T3 then fails or retries the pending turns, and starts a fresh app-server on the next turn. No backend restart was needed. Delegating to Claude instead of Codex avoids it.
Filed by
Claude Opus 5.5 · Claude Code · orchestrated by T3 Code (t3 triage). AI-generated; reviewed by @esukram before submission
Activity
[-]Codex notification handlers block the app-server stdout reader, so a busy permit makes the shared app-server drop its T3 connection[/-][+][Bug]: Codex notification handlers block the app-server stdout reader, so a busy permit makes the shared app-server drop its T3 connection[/+]juliusmarminge commentedon Nov 13, 2026
A fix for this was proposed in #28711, but when the reported steps were run in the real app the problem did not appear.
What was run on
f313d0a6cb, the commit before this change:On each commit, asked an orchestrating agent to delegate fifteen sequential Codex reviews with long streamed reports, then queued two follow-ups during the first review. Both completed all fifteen and processed both follow-ups. Follow-up wording started additional sequences, so the same clarification was steered to both agents after their original fifteen finished. Base completed four additional reviews; head completed one additional review before cancellation took effect. Persisted incremental updates confirm streaming. Neither side exhibited EAGAIN, dropped-message symptoms, or a permanent connection failure.
How: Real T3 development servers in separate checkouts with fresh isolated databases, driven through T3's project/thread CLI. Both used Codex CLI 0.166.0, gpt-6-luna, separate authenticated Codex homes, and one shared app-server session per server. Synthetic cache.js fixture; no replay provider or tests used as evidence.
Could you share the exact steps, what you see, and the version and platform you are on? The pull request is closed for now; the fix is picked up again once the problem can be shown.
juliusmarminge commentedon Nov 13, 2026
@esukram Can you capture T3's server log at the moment of a drop, with its timestamps next to the Codex EAGAIN line? We most need to know whether the drop came right after a Codex error, a capacity retry (serverOverloaded), a goal update, or a Stop/interrupt. Also: can you still reproduce it on a current nightly, and how many Codex threads were streaming at once when it happened?