Repository navigation
Description
What version of Codex CLI is running?
0.137.0
What subscription do you have?
pro
Which model were you using?
gpt-5.5
What platform is your computer?
Linux 5.15.120.bsk.3-amd64 x86_64
What terminal emulator and version are you using (if applicable)?
tmux
Codex doctor report
{
"schemaVersion": 1,
"generatedAt": "1780985871s since unix epoch",
"overallStatus": "warning",
"codexVersion": "0.137.0",
"checks": {
"app_server.status": {
"id": "app_server.status",
"category": "app-server",
"status": "ok",
"summary": "background server is not running",
"details": {
"control socket": "~/.codex/app-server-control/app-server-control.sock",
"daemon state dir": "~/.codex/app-server-daemon",
"mode": "ephemeral",
"pid file": "~/.codex/app-server-daemon/app-server.pid (missing)",
"settings": "~/.codex/app-server-daemon/settings.json (missing)",
"status": "not running",
"update-loop pid file": "~/.codex/app-server-daemon/app-server-updater.pid (missing)"
},
"remediation": null,
"durationMs": 0
},
"auth.credentials": {
"id": "auth.credentials",
"category": "auth",
"status": "ok",
"summary": "auth is configured",
"details": {
"auth file": "~/.codex/auth.json",
"auth storage mode": "File",
"stored API key": "false",
"stored ChatGPT tokens": "true",
"stored agent identity": "false",
"stored auth mode": "chatgpt"
},
"remediation": null,
"durationMs": 0
},
"config.load": {
"id": "config.load",
"category": "config",
"status": "ok",
"summary": "config loaded",
"details": {
"CODEX_HOME": "~/.codex",
"config.toml": "~/.codex/config.toml",
"config.toml parse": "ok",
"cwd": "<worktree-root>/<project>",
"enabled feature flags": "shell_tool, unified_exec, shell_snapshot, sqlite, memories, hooks, enable_request_compression, network_proxy, multi_agent, apps, tool_suggest, plugins, in_app_browser, browser_use, browser_use_external, computer_use, plugin_sharing, image_generation, skill_mcp_dependency_install, steer, guardian_approval, goals, collaboration_modes, tool_call_mcp_elicitation, personality, fast_mode, tui_app_server, workspace_dependencies",
"feature flag overrides": "terminal_resize_reflow=false, memories=true, network_proxy=true",
"feature flags enabled": "28",
"legacy feature flag": "codex_hooks -> hooks",
"log dir": "~/.codex/log",
"mcp servers": "1",
"model": "gpt-5.5",
"model provider": "openai",
"sqlite home": "~/.codex"
},
"remediation": null,
"durationMs": 0
},
"git.environment": {
"id": "git.environment",
"category": "git",
"status": "ok",
"summary": "git version 2.39.5",
"details": {
".git entry": "file -> <repo-cache>/<project>.git/worktrees/<project>",
"PATH git #1": "/usr/bin/git",
"PATH git #2": "/bin/git",
"PATH git entries": "2",
"git branch": "<feature-branch>",
"git build options": "git version 2.39.5; cpu: x86_64; no commit associated with this build; sizeof-long: 8; sizeof-size_t: 8; shell-path: /bin/sh",
"git exec path": "/usr/lib/git-core",
"git version": "git version 2.39.5",
"repo detected": "true",
"repo root": "<worktree-root>/<project>",
"selected git": "/usr/bin/git"
},
"remediation": null,
"durationMs": 33
},
"installation": {
"id": "installation",
"category": "install",
"status": "ok",
"summary": "installation looks consistent",
"details": {
"PATH codex #1": "~/.nvm/versions/node/v24.13.0/bin/codex",
"current executable": "<nvm-root>/lib/node_modules/@openai/codex/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/bin/codex",
"install context": "npm",
"managed by bun": "false",
"managed by npm": "true",
"managed package root": "<nvm-root>/lib/node_modules/@openai/codex",
"npm update target": "~/.nvm/versions/node/v24.13.0/lib/node_modules/@openai/codex"
},
"remediation": null,
"durationMs": 76
},
"mcp.config": {
"id": "mcp.config",
"category": "mcp",
"status": "ok",
"summary": "MCP configuration is locally consistent",
"details": {
"configured servers": "1",
"disabled servers": "0",
"stdio servers": "1"
},
"remediation": null,
"durationMs": 0
},
"network.env": {
"id": "network.env",
"category": "network",
"status": "ok",
"summary": "network-related environment looks readable",
"details": {
"proxy env vars present": "HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, NO_PROXY, no_proxy"
},
"remediation": null,
"durationMs": 0
},
"network.provider_reachability": {
"id": "network.provider_reachability",
"category": "reachability",
"status": "ok",
"summary": "active provider endpoints are reachable over HTTP",
"details": {
"ChatGPT base URL": "https://chatgpt.com/backend-api/ reachable (HTTP 403)",
"reachability mode": "ChatGPT auth"
},
"remediation": null,
"durationMs": 454
},
"network.websocket_reachability": {
"id": "network.websocket_reachability",
"category": "websocket",
"status": "ok",
"summary": "Responses WebSocket handshake succeeded",
"details": {
"DNS": "1 IPv4, 1 IPv6, first IPv4",
"auth mode": "chatgpt",
"connect timeout": "15000 ms",
"endpoint": "wss://chatgpt.com/backend-api/<redacted>",
"handshake result": "HTTP 101 Switching Protocols",
"model provider": "openai",
"models etag present": "true",
"provider name": "OpenAI",
"proxy env vars present": "HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, NO_PROXY, no_proxy",
"reasoning header": "false",
"server model present": "false",
"supports websockets": "true",
"wire API": "responses"
},
"remediation": null,
"durationMs": 1003
},
"runtime.provenance": {
"id": "runtime.provenance",
"category": "runtime",
"status": "ok",
"summary": "running npm on linux-x86_64",
"details": {
"commit": "unknown",
"install method": "npm",
"platform": "linux-x86_64",
"version": "0.137.0"
},
"remediation": null,
"durationMs": 0
},
"runtime.search": {
"id": "runtime.search",
"category": "search",
"status": "ok",
"summary": "search is OK (bundled)",
"details": {
"search provider": "bundled"
},
"remediation": null,
"durationMs": 0
},
"sandbox.helpers": {
"id": "sandbox.helpers",
"category": "sandbox",
"status": "ok",
"summary": "sandbox configuration is readable",
"details": {
"approval policy": "OnRequest",
"filesystem sandbox": "unrestricted",
"network sandbox": "enabled"
},
"remediation": null,
"durationMs": 0
},
"state.paths": {
"id": "state.paths",
"category": "state",
"status": "ok",
"summary": "state paths and databases are inspectable",
"details": {
"CODEX_HOME": "~/.codex (dir)",
"active rollout files": "137 files, 179334758 total bytes",
"archived rollout files": "2 files, 92761 total bytes",
"goals DB": "~/.codex/goals_1.sqlite (file) integrity ok",
"log DB": "~/.codex/logs_2.sqlite (file) integrity ok",
"memories DB": "~/.codex/memories_1.sqlite (file) integrity ok",
"state DB": "~/.codex/state_5.sqlite (file) integrity ok"
},
"remediation": null,
"durationMs": 562
},
"state.rollout_db_parity": {
"id": "state.rollout_db_parity",
"category": "threads",
"status": "ok",
"summary": "rollout files and state DB thread inventory agree",
"details": {
"default model provider": "openai",
"rollout DB active files": "137",
"rollout DB active rows": "137",
"rollout DB archived files": "2",
"rollout DB archived rows": "2",
"rollout DB model providers": "openai=139",
"rollout DB rows": "139",
"rollout DB sources": "vscode=98, exec=23, cli=10, subagent:review=5, subagent:thread_spawn=2, subagent:other=1"
},
"remediation": null,
"durationMs": 5479
},
"system.environment": {
"id": "system.environment",
"category": "system",
"status": "ok",
"summary": "OS language en-US",
"details": {
"LANG": "zh_CN.UTF-8",
"LC_ALL": "en_US.UTF-8",
"LC_CTYPE": "UTF-8",
"os": "Linux [64-bit]",
"os language": "en-US",
"os type": "Linux",
"os version": "Unknown"
},
"remediation": null,
"durationMs": 11
},
"terminal.env": {
"id": "terminal.env",
"category": "terminal",
"status": "ok",
"summary": "terminal metadata was detected",
"details": {
"COLORTERM": "truecolor",
"FORCE_COLOR": "3",
"TERM": "tmux-256color",
"color output": "disabled (stdout is not a terminal)",
"effective locale": "en_US.UTF-8",
"stderr is terminal": "false",
"stdin is terminal": "false",
"stdout is terminal": "false",
"terminal": "unknown",
"terminal size": "80x24"
},
"remediation": null,
"durationMs": 2
},
"terminal.title": {
"id": "terminal.title",
"category": "title",
"status": "ok",
"summary": "terminal title default",
"details": {
"terminal title activity": "true",
"terminal title items": "activity, project-name",
"terminal title project source": "git repo root",
"terminal title project value": "<project>",
"terminal title source": "default"
},
"remediation": null,
"durationMs": 0
},
"updates.status": {
"id": "updates.status",
"category": "updates",
"status": "warning",
"summary": "update configuration is locally consistent",
"details": {
"cached latest version": "0.137.0",
"check for update on startup": "true",
"last checked at": "2026-06-08T17:54:25Z",
"latest version probe": "curl: (22) The requested URL returned error: 403",
"update action": "npm install -g @openai/codex",
"version cache": "~/.codex/version.json"
},
"remediation": null,
"durationMs": 470
}
}
}What issue are you seeing?
When Codex runs inside a git worktree (.git is a file, not a directory), project-level hooks defined in <worktree-root>/.codex/hooks.json are silently not loaded or executed. Only user-level hooks from ~/.codex/hooks.json fire.
I tested three projects on the same machine with identical user-level hooks config:
| Project | .git type |
Project hooks.json SessionStart hooks |
Actual SessionStart hooks fired |
|---|---|---|---|
| test_case | directory (normal repo) | 1 | 3 (2 user + 1 project) |
| crm_space | file (worktree) | 2 | 2 (user only, 0 project) |
| infra_product | file (worktree) | 2 | 2 (user only, 0 project) |
Key observations:
--dangerously-bypass-hook-trustdoes not change the result β still only 2 hooks in worktree mode. This rules out a trust issue.codex doctorcorrectly identifies the worktree as a project (project source: git repo root, showing the worktree path).- The
.codex/hooks.jsonfile exists and is valid JSON at the worktree root. infra_producthas 13trusted_hashentries in~/.codex/config.tomlfor its project hooks β they still don't fire.
No error messages are shown. The project hooks are simply not discovered.
What steps can reproduce the bug?
# 1. Create a repo with a worktree
cd /tmp && mkdir hook-repro && cd hook-repro
git init main && cd main
git config user.email "test@test.com" && git config user.name "test"
echo "hello" > file.txt && git add . && git commit -m "init"
git worktree add ../wt -b feature
# 2. Add project-level hooks to the worktree
cd ../wt
mkdir -p .codex
cat > .codex/config.toml << 'EOF'
[features]
hooks = true
EOF
cat > .codex/hooks.json << 'EOF'
{
"hooks": {
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "echo PROJECT_HOOK_FIRED >> /tmp/codex-hook-probe.log"
}
]
}
]
}
}
EOF
# 3. Run codex in the worktree
rm -f /tmp/codex-hook-probe.log
codex exec --dangerously-bypass-hook-trust "echo hello"
# 4. Check
cat /tmp/codex-hook-probe.log
# Expected: contains "PROJECT_HOOK_FIRED"
# Actual: file does not exist β project hook never fired
# 5. Compare: do the same in the main (non-worktree) repo
cd ../main
mkdir -p .codex
cp ../wt/.codex/config.toml .codex/
cp ../wt/.codex/hooks.json .codex/
rm -f /tmp/codex-hook-probe.log
codex exec --dangerously-bypass-hook-trust "echo hello"
cat /tmp/codex-hook-probe.log
# Result: contains "PROJECT_HOOK_FIRED" βWhat is the expected behavior?
Project-level .codex/hooks.json placed at the worktree working tree root (the path returned by git rev-parse --show-toplevel) should be discovered and loaded, the same way it works in a normal (non-worktree) repository.
Additional information
Root cause hypothesis: In a git worktree, .git is a file containing gitdir: /path/to/.git/worktrees/<name>. The hooks discovery code likely resolves the project root by looking for a .git directory, or by using git rev-parse --git-common-dir (which returns the main repo's .git path), instead of using git rev-parse --show-toplevel (which correctly returns the worktree working tree root). This causes the hooks scanner to look for .codex/hooks.json at the wrong location.
Evidence supporting this: I found a stale trusted_hash entry in ~/.codex/config.toml keyed to a .codex/hooks.json path at the bare repo cache parent directory (where --git-common-dir resolves), not at the worktree working tree β suggesting codex previously resolved the hooks config path to that location.
Workaround: Register hooks at the user level (~/.codex/hooks.json) instead of project level. User-level hooks fire correctly regardless of worktree status.
Activity
github-actions commentedon Jun 9, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
[-]`codex_hooks` do not fire in interactive sessions when configured via repo-local `.codex/config.toml`[/-][+] Project-level .codex/hooks.json is silently ignored when Codex runs inside a git worktree[/+]woolkingx commentedon Jun 11, 2026
This worktree failure mode is exactly why I ended up building
codex-hooksas a schema-first operating layer around Codex hooks: not to replace upstream hooks, but to make discovery/runtime state inspectable.Repo: https://github.com/woolkingx/codex-hooks
For this specific issue, the useful piece would be a
doctorcheck that records:git rev-parse --show-toplevel.gitentry type (filevs directory)That would make the bug show up as "project hook source not discovered for worktree cwd" instead of silently falling back to user hooks. I can add this as a focused repro/doctor check on my side and link the output here if it helps triage.
Necmttn commentedon Jun 21, 2026
A focused regression would catch this cleanly:
.codex/hooks.jsonSessionStartdiscovers and dispatches the project hookThe other useful fix is observability.
codex doctoror a hooks list should show session cwd,git rev-parse --show-toplevel, whether.gitis a file or directory, each discovered hook source path, and the trust key/source used for that hook.Then this failure becomes visible as "project hook source not discovered for worktree cwd" instead of silently falling back to user-level hooks.
Generated with ax.
nyavana commentedon Jun 25, 2026
This is happening to me as well when using https://github.com/mindfold-ai/trellis
marenostrum1981 commentedon Jul 15, 2026
Happening too when using v.0.144.0
wakadorimk2 commentedon Aug 7, 2026
We reproduced the same worktree-specific behavior in a Windows Quiet Days repository.
Observed:
.codex/hooks.jsonexists at the worktree root.git rev-parse --show-toplevelreturns that worktree root..gitentry is a file containing the linked-worktree gitdir./hooksshows the project-localPostCompactandSessionStartentries as0 installed / 0 active, while user-level hooks remain visible.--dangerously-bypass-hook-trustdoes not repair worktree discovery; the failure appears to occur before hook trust/dispatch.An experimental adapter and tests are preserved here:
https://github.com/wakadorimk2/quiet-days/tree/codex/goal-execution-state-v3-experimental
No private filesystem paths or secrets are included.
nordscope-fi commentedon Aug 9, 2026
Still present on 0.147.0 (macOS), and the rule looks narrower than "ignored in a worktree": the project hook path resolves to the main working tree.
hooks/listovercodex app-serveranswers this without spending a model turn, which makes it easy to separate discovery from trust.Setup
codex-cli 0.147.0, macOS 26.5.2, arm64.codex/hooks.jsoncommitted, thengit worktree addCODEX_HOME,hooks/listcalled withcwds: ["<worktree>"].gitis a file, as expected for a linked worktreeWhat
hooks/listreports from inside the worktreeconfig.tomlsourcePath.codex/hooks.json.codex/hooks.jsonBoth trust spellings work, and in both cases the file that gets loaded is the main checkout's. The worktree's own
.codex/hooks.json, which is a real committed file sitting at its root, is never the source.The decisive case
Remove
.codex/hooks.jsonfrom the main checkout and leave the worktree's own copy in place, with both paths trusted:So the worktree's file is not merely lower-priority. It is invisible.
Why this reads as "silently ignored"
Anyone who runs an installer inside a worktree, or edits hooks on a branch checked out as one, gets exactly the reported symptom: a correct-looking
.codex/hooks.jsonat the repository root,/hooksshowing nothing from the project, and no error anywhere. If the main checkout happens to have a file too, something loads, and it is the wrong one, which is arguably worse than nothing.This matches the earlier comment about a stale
trusted_hashkeyed to the path--git-common-dirresolves to rather than to the working tree.Two suggestions, in order of how much they would help:
git rev-parse --show-toplevelrather than the common git directory..codex/hooks.jsonexists at the current working tree root and is not the file being loaded. The whole cost of this bug is that it says nothing.fordb commentedon Aug 18, 2026
Confirmed on macOS with bundled
codex-cli 0.146.0-alpha.3.1; this can produce a convincing false diagnosis of unrelated lifecycle hooks.Black-box comparison:
.codex/hooks.jsoncontainedUserPromptSubmit,PreCompact,PostCompact, andSessionStart(source=compact)handlers. The main checkout's version contained the olderUserPromptSubmithandlers only.codex exec -C <worktree> --dangerously-bypass-hook-trustdisplayed twoUserPromptSubmitruns, then visibly auto-compacted after a shell tool call, but displayed noPreCompactorPostCompactrun.UserPromptSubmit, thenPreCompact Completed,context compacted,PostCompact Stopped, andturn interruptedbefore the requested final response.This isolates config discovery rather than hook command behavior: in the worktree run, the number and event set of discovered hooks matched the main checkout's
.codex/hooks.json; in the standalone run, they matched the disposable repo's file. Trust bypass did not make the worktree-local project layer win.No private transcript or prompt/response content was inspected or uploaded; the comparison used visible hook status rows plus metadata-only local receipts.
abhijit-mehta commentedon Aug 25, 2026
Codex / GPT-5.6 Sol corroboration: this is still present in Codex CLI 0.149.1 on macOS arm64.
I ran an A/B test that isolates the lookup root:
.codex/hooks.json; the primary checkout contained no.codex/directory./hooksshowedPreToolUse 0 installed / 0 active. Exact project trust and--dangerously-bypass-hook-trustdid not change discovery.hooks.jsoninto the primary checkout./hookschanged toPreToolUse 1 installed.codex exec --dangerously-bypass-hook-trustfrom the linked worktree then blockedgit push --dry-run origin mainbefore Git executed.printfcanary showedPreToolUse Completedand ran successfully. The handler command used$(git rev-parse --show-toplevel), so execution correctly resolved the linked worktree's script.codex doctorcorrectly reported the linked worktree asrepo rootand showed its.gitentry pointing into the primary checkout's shared git directory.This confirms the split described in #23996: discovery is anchored to the primary checkout, while execution cwd/git-root resolution is anchored to the linked worktree. It is not only a missing-discovery case; when both branches have hook files, Codex can load policy from one branch and execute code from another.
That creates two risky failure modes:
The current official docs list
<repo>/.codex/hooks.jsonas a supported location and say/hookslists configured hooks before trust decisions: https://learn.chatgpt.com/docs/hooks. Resolving discovery against the current worktree'sgit rev-parse --show-toplevelwould match that contract. At minimum, Codex should warn when<current-worktree>/.codex/hooks.jsonexists but a different source is loaded (or none is loaded).The practical workaround is to install the definition in the primary checkout or user hook layer, but neither preserves branch-local hook semantics.
wesleywsl1993 commentedon Sep 27, 2026
Codex CLI 0.157.1 / Windows 11
project config layer = enabled
project hooks.json = valid
inline project hook = valid
features.hooks = true
managed-only = false
expected:
project hooks listed in /hooks
actual:
only ~/.codex/hooks.json is discovered
/debug-config:
project .codex/config.toml = enabled
.codex/hooks.json:
valid JSON
valid schema
features.hooks = true
/project inline hook in config.toml:
also not discovered
/hooks:
only ~/.codex/hooks.json appears
Main symptom:
project hooks Installed = 0
Worktree:
.git is a file / linked worktree
aitrawog commentedon Oct 10, 2026
Confirmed on Codex CLI 0.162.1 (Linux) from a linked Git worktree. The worktree contains a valid project
.codex/hooks.json; the exact worktree path is trusted in user config,/debug-configshows the project.codex/config.tomllayer enabled, andcodex doctorreports hooks enabled. In the interactive CLI,/hookslists 0 installed / 0 active for project events. A plaincodex exec --ephemeralrun from that worktree returnedNOT INJECTEDwhen asked for the first heading emitted by the SessionStartadditionalContext, without reading local files. This matches the documented worktree discovery failure and rules out hook trust as the cause.joaodibba commentedon Oct 11, 2026
Same on Codex CLI 0.162.1, macOS arm64.
.codex/hooks.json(onePreToolUsehook, matcher^(Bash|apply_patch)$, acommandhandler). The primary checkout has no.codex/./hooksin the worktree shows "No hooks installed" forPreToolUse. Only~/.codex/hooks.jsonhooks are discovered.codex exec --dangerously-bypass-hook-trustfrom the worktree runs a shell command. The user-levelSessionStart/UserPromptSubmit/PostToolUse/Stophooks fire, but the projectPreToolUsehook never does.AGENTS.mdand.agents/skillsload fine, so only hook discovery uses the primary checkout's root.A fix would let teams commit shared hooks and have them apply in worktrees, which is where agents usually run.