codex_hooks do not fire in interactive sessions when configured via repo-local .codex/config.toml
Open 💬 8 comments Opened Apr 12, 2026 by hahnpierre
💡 Likely answer: A maintainer (github-actions[bot], contributor)
responded on this thread — see the highlighted reply below.
Bug Report Draft
Title
codex_hooks do not fire in interactive sessions when configured via repo-local .codex/config.toml
Summary
Repo-local .codex/config.toml appears to be loaded for normal project configuration, but hook configuration inside that file does not result in interactive SessionStart or Stop hooks firing.
Environment
- Codex CLI version:
0.120.0 - Installation: npm global package
- Platform: macOS Apple Silicon
- Repo uses project-local config:
./.codex/config.toml
Reproduction
- Create a repo-local
.codex/config.tomlwith:
hooks = "/absolute/path/to/repo/.codex/hooks.json"
[features]
codex_hooks = true
- Create
hooks.jsonwithSessionStartandStopcommand hooks. - Point the hook commands at scripts that:
- read hook payload JSON from stdin
- emit valid hook JSON on stdout
- write debug artifacts to disk so execution can be confirmed
- Start an interactive session with
codexfrom the repo root. - Try to confirm:
- startup context injection
- stop-time block/reminder behavior
Expected
SessionStartshould fire when the interactive session startsStopshould fire when the interactive session exits- hook output should affect the live session just like manual hook execution output does
Actual
- interactive sessions start and stop normally
- no evidence that
SessionStartfired - no evidence that
Stopfired - no startup
additionalContextappears in the session rollout log - no stop block/reminder occurs
Important Control Result
The same hook scripts work when run manually:
SessionStartscript emits valid JSON withhookSpecificOutput.additionalContextStopscript emits valid JSON withdecision = "block"and areason- debug artifacts are written correctly
This suggests the problem is not the script payload format itself, but that repo-local hook config is not actually being honored by interactive hook execution.
Notes
- Moving
hooks = ".../hooks.json"under[features]is invalid and causes a TOML type error. The correct placement is top-level. - The repo-local config format itself parses cleanly once
hooksis top-level. - The issue affects at least both
SessionStartandStop, which suggests a config-loading or hook-registration problem rather than an event-specific matcher issue.
8 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
can confirm. faced the same issue.
Same
Just setup a Stop hook locally and I'm not able to repro
Here's my local setup:
As well as how my conversation looks
Would be super helpful if you could attach more details, thank you!
---
Also FWIW we don't support a custom path to a
hooks.jsonvia top-levelhooksfield in theconfig.toml. Codex will resolve thehooks.jsonby looking in the same directories as any enabled config layers.same
+1 — confirmed reproducible on Codex CLI 0.128.0 (macOS, ChatGPT auth). Adding empirical detail in case it helps narrow the cause.
Test setup
Minimal probe hook at
/tmp/codex-probe.sh:Registered at
~/.codex/hooks.json:~/.codex/config.tomlhas[features] codex_hooks = true(also confirmed default-true in v0.128.0 source).Results
| Invocation | Probe log written? | systemMessage shown? |
codex_hooksdebug log ||---|---|---|---|
|
codex exec 'echo test'| ✅ yes | n/a (non-interactive) |hook: SessionStart+hook: SessionStart Completed||
codex(interactive TUI) | ❌ no | ❌ no | (no hook log lines) |So the discovery + parsing path works (since
execfinds and runs the samehooks.json), but the interactive TUI startup path skips hook execution entirely. Same~/.codex/hooks.json, same~/.codex/config.toml, same probe script — only the launch mode differs.Reproduction
codex exec --skip-git-repo-check 'echo'→/tmp/codex-probe.loggets a linecodex(interactive) → no log entry, no system message~/.codex/and project./.codex/) — same resultThis affects cross-platform tooling that targets all three of Claude Code / Gemini CLI / Codex via SessionStart hooks (e.g. memory injection at session start). Header + context render fine on Claude and Gemini interactive sessions, silently fail on Codex interactive.
Happy to PR a fix if you can point at where the interactive TUI startup path should call into the hook engine.
Indexed this hook ticket in the umbrella tracker: #21753
Goal: collect the scattered Codex hook requests and bugs into one parity matrix for Full Claude Code Hook Parity (29+), while preserving this issue as the detailed thread for its specific behavior.
Confirming this is still reproducible on codex CLI 0.132.0 (macOS arm64, ChatGPT auth, 2026-05-21), and adding empirical data that may help narrow the cause.
Updates vs previous comments
The thread previously stopped at 0.128.0 with the issue still open. The behavior is unchanged on 0.132 across both modes:
| Mode | Result |
|---|---|
|
codex exec(headless CLI) with--dangerously-bypass-hook-trust+[projects."<ws>"] trust_level = "trusted"pre-seeded in$CODEX_HOME/config.toml| hook command never executes ||
codex(interactive TUI via tmux) — accept directory trust dialog → accept "Trust all and continue" hook trust dialog →trusted_hashIS written to$CODEX_HOME/config.toml→ restart codex (trust persists) → submit a turn | hook command never executes |So the per-hook trust storage layer works correctly, and the directory-trust UX path works correctly — but the hook executor itself never runs the command for either mode.
Codebase observation (may help triage)
In
codex-rs/hooks/, thecodex_hookscrate contains zerotracing::calls acrossengine/mod.rs,engine/discovery.rs,registry.rs, andhook_runtime.rs. WithRUST_LOG=info,codex_hooks=tracethecodex-tui.logfor an interactive session showssession_init/shell_snapshot/mcp_manager_init/ etc., but nothing about hook discovery, loading, or firing — even with hooks visibly registered in the TUI Hooks dialog (Installed: 1, Active: 0, Review: 1before trust →Installed: 1, Active: 1, Review: 0after). This makes any kind of behavioral debugging from user-side opaque.The Rust unit tests in
codex-rs/core/tests/suite/hooks.rsall drive codex via the in-processtest_codex().submit_turn()library API, never through theexec/tuiCLI binaries. So the existing test matrix wouldn't catch a regression in the CLI → core wiring ofrun_pending_session_start_hooks.Reproduction script
End-to-end matrix test that exercises both modes and reports per-mode PASS/FAIL:
https://github.com/swchen44/c_code_scanner_for_memory/tree/main/tools/codex_hook_firing_repro
The script:
$CODEX_HOMEwithauth.json+hooks.jsoncontaining aSessionStarthook that does/usr/bin/touch <marker>codex execmode with all trust gates explicitly bypassedcodexTUI mode via tmux, programmatically accepting both trust dialogsOn my machine both modes return FAIL. Equivalent SessionStart hooks for Claude Code (
.claude/settings.json) and OpenCode (TS plugin via event bus) on the same machine fire correctly — so the workspace, environment, and hook script itself are all fine. It's specifically the codex client-side dispatch that doesn't deliver the event.Impact (for context)
For an internal usage-tracking integration we built, the SessionStart hook silently no-ops on codex sessions, so dashboards undercount codex adoption relative to CC + OpenCode. We've shipped all the client-side mitigations we can (per-test
CODEX_HOMEisolation, project trust pre-seed,--dangerously-bypass-hook-trustflag) — none of it helps because the executor itself never runs.Happy to provide additional traces, test on a nightly build, or refine the repro script if any of that would help.