SessionStart hook does not fire when bare `codex` auto-restores the previous thread (0.130.0)

Open 💬 1 comment Opened May 23, 2026 by exi-pm-agent[bot]

Summary

On Codex CLI 0.130.0, bare codex launch in a terminal where a residual Codex process is present (for example after closing the TUI without quitting, or relaunching from the same shell) auto-restores the previous thread and does not emit any of the documented SessionStart source values (startup, resume, clear, compact). As a result, configured SessionStart hooks never fire on this "soft restart" path.

Environment

Reproduction

  1. Configure a SessionStart hook in ~/.codex/hooks.json:
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume|clear",
        "hooks": [
          {
            "type": "command",
            "command": "$HOME/path/to/hook.sh",
            "timeout": 60,
            "statusMessage": "running"
          }
        ]
      }
    ]
  }
}

Have the hook print a unique marker to stderr or emit a systemMessage so its firing is observable.

  1. Trust the hook via /hooks in the Codex TUI.
  1. Launch codex once. The hook fires (cold start). A new rollout file appears under ~/.codex/sessions/<date>/.
  1. Without fully quitting the prior session (close terminal window, or leave the TUI in the background, or relaunch from the same shell), launch codex again. The hook does NOT fire. No statusMessage appears.
  1. Confirm a residual Codex process is present:
pgrep -f "@openai/codex"

Inspect ~/.codex/log/codex-tui.log: the new TUI continues using the previous thread_id without emitting any new SessionStart event.

Workaround

Fully kill all Codex processes before launching:

pkill -f "@openai/codex"
sleep 1
pgrep -f "@openai/codex" && echo "still running" || codex

After a true cold start, the SessionStart hook fires and the configured additionalContext and systemMessage surface as expected. A shell wrapper that wraps codex to perform this kill-before-launch automatically restores the contract.

Expected behaviour

Bare codex launch that picks up the previous thread should emit one of the documented source values (most naturally resume) so a SessionStart matcher fires the hook. The current behaviour is a fourth, implicit "soft restart" path that bypasses the documented contract.

Why this matters

For installations that use SessionStart hooks to render a startup dashboard, enforce a posture gate, or load context, the soft-restart path silently bypasses those guarantees. The hook contract documented at https://developers.openai.com/codex/hooks lists startup|resume|clear|compact; auto-restore on bare launch does not match any of them.

Suggested fix

Have Codex emit source: "resume" when bare launch restores a previous thread, so the existing matcher pattern fires the hook. Alternatively, document the soft-restart path as an intentional bypass so users can configure a complementary mechanism.

Investigation reference

Full investigation (sources, evidence, ruled-out hypotheses, durable workaround) is documented internally at Experience-Innovation/00_exi-foundation/docs/governance/codex-0130-soft-restart-hook-gap-2026-05-23.md (private repo). Key findings:

  • Hook script integrity verified: direct invocation produces correct JSON output.
  • Trust state verified: re-trusting in /hooks did not change the symptom.
  • Cold-start test verified: after a clean kill of all Codex processes, the hook fires correctly on next launch.
  • The same Codex installation correctly renders systemMessage (TUI warning) and hookSpecificOutput.additionalContext (developer message) when the hook fires; only the firing itself is missing on soft restart.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗