Skip to content

fix(server): a hung Codex app-server no longer blocks every later Codex run - #26555

Merged
juliusmarminge merged 3 commits into
pingdotgg:mainfrom
MahirAbbas:fix/codex-initialize-timeout
Nov 14, 2026
Merged

juliusmarminge merged 3 commits into
pingdotgg:mainfrom
MahirAbbas:fix/codex-initialize-timeout

Conversation

@MahirAbbas

@MahirAbbas MahirAbbas commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Problem

All Codex threads on one instance share one codex app-server session. If that app-server hangs before it answers initialize, the request waits forever while it holds the session's initialize permit (CodexAdapterV2.ts, ensureInitialized). Every later Codex run reuses the same live session (ProviderSessionManager.open returns the existing entry) and waits behind the permit. The runs stay in "starting" with startedAt: null until T3 restarts. A new Codex binary path does not help, because the live session is reused. Status probes use fresh processes, so Codex looks healthy the whole time.

This is a small, focused fix for an obvious hang: a request with no timeout that holds a shared lock.

How I established it

This happened on T3 Code Nightly 0.1.2-nightly.20261021 on macOS. A hung syspolicyd made every new exec of codex stop at _dyld_start. The server traces show:

  • The first Codex run opened the shared session at 21:48:51 (CodexAdapterV2.open succeeded). Its first CodexAppServerProtocol.request never got an answer.
  • No CodexAdapterV2.open ran again until the restart at 22:16. Three delegated Codex runs and two other runs each stopped at the first CodexAppServerProtocol.request in ProviderTurnStartService.loadFromProvider. They waited 64 s to 414 s, and each one ended only when it was cancelled.
  • The permit serialized the waiters. Run cbea0d64 sent its request 3 ms after the previous waiter was cancelled.
  • After the restart, the first Codex run completed in 1.5 s.

Claude runs kept working the whole time, so the effect worker was not blocked.

Fix

initialize now times out after 30 seconds. It normally answers in under a second. On timeout, the adapter fails the session's event stream, the same signal #26188 uses when the app-server exits.

Since #27281, initialize runs in two places, and the timeout covers both:

  • During open (standard Codex driver, which checks the Codex version). The hang held the session open lock. Now the open fails, and the session manager discards the session and stops the app-server.
  • On the first thread (managed Codex). The hang held the initialize permit. Now the failed event stream makes the session manager retire the session and stop the app-server.

In both cases the stuck run fails instead of staying in "starting", and the next run spawns a fresh app-server with the current settings. The event queue is now created before initialize, because the timeout handler can run during open.

Open PR #26253 (one app-server per thread) would limit this hang to one thread. It does not add a timeout, so that thread would still hang.

Verification

Two new tests in CodexAdapterV2.test.ts, each with a replay app-server that never answers initialize:

  • fails the open when initialize never answers during open: the open fails after 30 s (TestClock).
  • retires the shared session when initialize never answers: ensureThread fails after 30 s, and the session event stream fails.

Results:

  • Without the fix, both tests hang until the test timeout.
  • With the timeout but without moving the event queue, the open test fails with ReferenceError: Cannot access 'events' before initialization.
  • With the fix, vp test run src/orchestration-v2/Adapters/CodexAdapterV2.test.ts passes 135 of 135.
  • tsc --noEmit and vpr build for apps/server pass. vp lint and vp fmt --check on the two changed files are clean.

Not checked: I did not reproduce the hang end to end in a running T3 instance.

Model: Claude Opus 5.5 in Claude Code, running in T3 Code.

🤖 Generated with Claude Code

@github-actions github-actions Bot added the vouch:unvouched Author is not yet trusted in the VOUCHED list. label Oct 28, 2026
@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

Codex app-server initialization now has a 30-second timeout. If initialization does not respond, the adapter fails the event stream and initialization request. Tests cover timeouts during session opening and in an opened session.

Changes: Codex initialization timeout

Layer / File(s) Summary
Initialization timeout and failure handling
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts, apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts
The adapter creates the event queue before initialization, returns a recorded timeout error rather than retrying initialization, and fails the queue if initialization exceeds 30 seconds. Tests cover failures during session opening and in an opened session.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: juliusmarminge

Merge Risk: Minimal · up to 995de

A hung Codex app-server initialization now times out after 30 seconds and the session is retired, so later runs can start a fresh app-server instead of blocking. No actionable merge-blocking risk remains.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title states the main change: a 30-second timeout on Codex initialize so a hung app-server no longer blocks later Codex runs. It follows the conventional commit format and matches the changeset an…
Description check Passed The description follows the template. It has Problem, Change, Scope, and Verification sections. The Problem section explains the hang and its effect on shared sessions. The Change section explains the…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title states the main change: a 30-second timeout on Codex initialize so a hung app-server no longer blocks later Codex runs. It follows the conventional commit format and matches the changeset and objectives.

Full details: Description check

Explanation

The description follows the template. It has Problem, Change, Scope, and Verification sections. The Problem section explains the hang and its effect on shared sessions. The Change section explains the 30-second timeout and the two initialize paths. Scope gives the bug-fix justification and notes the related PR. Verification lists the new tests, the commands run, the results, and what was not checked. It also names the model and harness, as the template requests. The description is complete and specific.

Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between bf5ad4b and 995de7e.

Files selected for processing: 2

  • apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts
  • apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

…ex run

All Codex threads on one instance share one app-server session. If that
app-server hangs before it answers `initialize`, nothing times out. During
open, the hang holds the session open lock. After open (managed Codex), it
holds the initialize permit. Either way every later Codex run waits behind
it and stays in "starting" until T3 restarts.

`initialize` now times out after 30 seconds and fails the session's event
stream. A failed open is discarded, and a failed stream makes the session
manager retire the session. Both stop the hung app-server, so the next run
spawns a fresh one. The event queue is now created before `initialize` can
run during open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MahirAbbas
MahirAbbas force-pushed the fix/codex-initialize-timeout branch from 3139860 to c65c7a6 Compare November 2, 2026 16:47
@github-actions github-actions Bot added the size:S 10-29 changed lines (additions + deletions). label Nov 2, 2026
@MahirAbbas

Copy link
Copy Markdown
Contributor Author

Rebased onto current main and updated for #27281.

What this PR does: if Codex's app-server hangs before it answers initialize, every later Codex run used to wait behind it and stay in "starting" until T3 restarted. initialize now times out after 30 seconds. The hung app-server is then thrown away, and the next run starts a fresh one.

What changed in this update:

  • Since fix: Intel Mac installs and broken Codex versions fail with clear next steps #27281, the standard Codex driver runs initialize while it opens the session, to check the Codex version. Managed Codex still runs it on the first thread. The timeout now covers both paths: a failed open is discarded, and a failed event stream retires the session.
  • The event queue is now created before initialize, because the timeout handler can run during open. Without this move the handler threw ReferenceError: Cannot access 'events' before initialization. The new open-path test catches it.
  • Kept main's new broken-version test next to the two new tests.

Checks: all 135 tests in CodexAdapterV2.test.ts pass, and tsc --noEmit and vpr build for apps/server pass. Lint and format are clean on both changed files.

@coderabbitai coderabbitai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Fix comments: Fix CodeRabbit comments on this PR

@maria-rcks

maria-rcks commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Note

Written by gpt-6.1-sol on behalf of Maria

Verified in a real dev environment

Verified unchanged head c65c7a6a1afa675fa446f011768524b22a540e36 on a Linux Boat VM, using repository Playwright with Google Chrome and real Codex 0.164.0 / gpt-6.1-sol through the local proxy. Created the git project and all threads through the web UI. No mocks, fixtures, or demo data.

Reproduced on github/main (f6503f0e71): a binary-path wrapper launches the actual Codex app-server and pauses its captured PID before initialization. Two user-submitted turns remained in starting with startedAt: null for 76.9s and 62.0s, until I stopped main. Removing the launch pause did not unblock them.

On this PR, the same pause triggers the initialize timeout at 30s and the stopped process is removed. After removing the pause, both pending threads complete without restarting T3: INIT_RECOVERY_OK and SECOND_THREAD_OK. The original thread also recalls its marker and successfully executes git status --short (exit 0).

There is one qualification to the description: the effect worker retries a failed open. A transient hang therefore recovers automatically; a persistent hang takes five 30s attempts and reaches the visible failed state after 152.1s, rather than 30s total. After that failure, removing the pause and submitting another prompt in the same thread returns RETRY_AFTER_FAILURE_OK without a server restart.

Blacksmith: vp i && vp test run apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts, 135/135 passed. No source changes or pushes were needed. GitCafe reports 29 successful checks; GitHub separately lists a cancelled native fingerprint run, so this verification does not claim every historical CI job succeeded.

Short runtime transcript

main: first turn remains working while two threads are blocked

pr: recovered real Codex reply, retained context, and successful git command

pr: two pending threads recover after the initialize timeout

pr: persistent launch failure reaches a visible error after bounded retries

Not verified

Managed ChatGPT Codex first-thread initialization and its concurrent-waiter retirement path were not exercised end to end: this VM has the standard Codex proxy setup, not a managed ChatGPT login. The adapter tests cover its timeout signal, but do not establish that live integration. Native desktop, iOS/Android, macOS/Windows, and the original macOS syspolicyd cause were not exercised. Linux process suspension reproduces the non-answering app-server behavior, not the OS-specific cause.

…once

After the first `initialize` timeout the session is retiring, but a thread
waiting on the initialize permit could still send another `initialize` to the
same hung app-server and wait up to 30 seconds more. The adapter now records
the timeout, and later waiters fail with the same error without a new request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). and removed size:S 10-29 changed lines (additions + deletions). labels Nov 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR conflicts with main. Rebase it or merge main into it. If it still conflicts after 7 days with no activity, it will be closed automatically.

See the conflict window.

@github-actions github-actions Bot added the needs-rebase Conflicts with its base branch. Closes after 7 days with no activity. label Nov 14, 2026
@juliusmarminge juliusmarminge added the takeover:in-progress A takeover lead is working on this PR label Nov 14, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@juliusmarminge

Copy link
Copy Markdown
Member

Evidence

Configured a suspending Codex wrapper, submitted identical prompts in two threads, removed the suspend flag, and submitted a third prompt. Both sides used fresh databases, real Codex gpt-6-luna, Chromium, a 1280×800 viewport, and light theme.

Before After
4121b30f78 995de7e32b
Before: third thread Starting after 3m15s, Working 3 After: third thread answered OK and earlier threads Done
Real-time recording: blocked runs, then SIGCONT control Real-time recording: automatic recovery

Base stayed blocked until manual SIGCONT. Head discarded unresponsive processes after approximately 30 seconds; all three runs subsequently answered OK without restarting T3 or manually signaling a process. The third head run completed approximately 33 seconds after submission. Original runs remained Starting during automatic retries; immediate visible terminal errors are not claimed.

Wrapper used · Reproduction recipe · Runtime summary · Timelines and capture details

Recordings begin after initial launches; initial settings edits are not recorded. Submission intervals differ between sides and are documented. Videos mask the private environment-name region. The before still contains a live timer and spinner; the video is the primary temporal evidence.

Not shown:

  • Desktop and mobile clients were not exercised; the shared server startup behavior was verified through the real web client.
  • The before still did not produce identical screenshots 500 ms apart because the elapsed timer and spinner remained active; the real-time recording documents the blocked state.

@juliusmarminge juliusmarminge added takeover:ready Takeover done; auto-lands when gates pass and removed takeover:in-progress A takeover lead is working on this PR labels Nov 14, 2026
@juliusmarminge
juliusmarminge merged commit f3b43ef into pingdotgg:main Nov 14, 2026
49 checks passed
@juliusmarminge juliusmarminge removed the takeover:ready Takeover done; auto-lands when gates pass label Nov 14, 2026
AIdoesmyjob pushed a commit to AIdoesmyjob/t3code that referenced this pull request Nov 14, 2026
…ex run (pingdotgg#26555)

Co-authored-by: Julius Marminge <julius0216@outlook.com>
(cherry picked from commit f3b43ef)
Pipyakas pushed a commit to Pipyakas/t3code that referenced this pull request Nov 15, 2026
…ex run (pingdotgg#26555)

Co-authored-by: Julius Marminge <julius0216@outlook.com>
(cherry picked from commit f3b43ef)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-rebase Conflicts with its base branch. Closes after 7 days with no activity. size:M 30-99 changed lines (additions + deletions). vouch:unvouched Author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants