SessionStart hook on resume can report `hook timed out after 60s` even when the hook exits 0 in ~4s

Resolved 💬 2 comments Opened May 3, 2026 by laffo16 Closed May 4, 2026

On Windows, Codex CLI resumed a session and showed:

  • SessionStart hook (failed)
  • error: hook timed out after 60s

For that same resumed session:

  • the outer SessionStart wrapper finished exit=0 in 4085ms
  • the inner hook finished outcome=success in 3218ms
  • the session was still running immediately afterward

This looks like a false or misattributed SessionStart timeout banner on the
resume path rather than a real 60s timeout of the successful hook invocation.

Environment

  • Codex CLI: 0.128.0
  • OS: Windows 10
  • Shell: PowerShell 7
  • Terminal emulator: Windows Terminal
  • [features].codex_hooks = true
  • [features].tui_app_server = true
  • Hook type: SessionStart command hook
  • Configured SessionStart timeout: 60

Hook Setup

The specimen was taken with a SessionStart command hook configured at
timeout: 60.

The hook ran through a PowerShell wrapper that:

  • logged start/end timing
  • synchronously invoked the real hook work
  • exited with the real hook result

Sanitized shape:

config.toml

[features]
codex_hooks = true
tui_app_server = true

hooks.json

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "& \"<sessionstart-wrapper>.ps1\"",
            "timeout": 60
          }
        ]
      }
    ]
  }
}

Observed

On resume, Codex displayed the failure banner above.

For the same resumed session id, local timing showed:

phase=start ... sessionId=<resumed-session-id> source=resume
phase=end ... sessionId=<resumed-session-id> source=resume exit=0 elapsedMs=4085

For that same resumed session id, the inner hook log showed:

event=SessionStart outcome=success ... sessionId=<resumed-session-id> ... totalMs=3218

Immediately afterward, a direct status check for that same resumed session id
returned:

{
  "found": true,
  "sessionId": "<resumed-session-id>",
  "state": "running"
}

Minimal Repro Shape

I do not yet have a tiny standalone repro outside my normal setup, but the
observed shape is:

  1. Enable a SessionStart command hook on Windows.
  2. Set timeout to 60.
  3. Run the hook through a wrapper that logs timing and then invokes the real

hook synchronously.

  1. Resume an existing Codex session.
  2. Observe that Codex may display:
  • SessionStart hook (failed)
  • error: hook timed out after 60s
  1. Compare the banner against hook-side timing for the same resumed session id.

In this specimen, hook-side timing contradicted the UI banner.

Expected

  • If the hook really times out, the same resumed-session invocation should not

later show successful completion.

  • If the hook completes successfully well inside the timeout window, Codex

should not show a timeout failure banner for that resumed session.

Evidence

1. User-visible failure

The terminal UI displayed:

SessionStart hook (failed)
error: hook timed out after 60s

2. Outer wrapper completed successfully for the resumed session

phase=start ... sessionId=<resumed-session-id> source=resume
phase=end ... sessionId=<resumed-session-id> source=resume exit=0 elapsedMs=4085

3. Inner hook completed successfully for the same resumed session

event=SessionStart outcome=success ... sessionId=<resumed-session-id> ... totalMs=3218

4. The resumed session was still running afterward

{
  "found": true,
  "sessionId": "<resumed-session-id>",
  "state": "running"
}

Possible Clue

Secondary clue only, not required for the main claim:

  • A separate startup-path wrapper invocation on a different internal session id

exited quickly with code 1.

  • That may explain the wrong banner attribution if Codex is mixing startup-path

and resume-path hook outcomes internally.

Suspected Issue

Codex may be surfacing stale SessionStart failure state on resume, or
attributing a different startup-path hook outcome to the resumed session UI.

View original on GitHub ↗

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