codex exec skips persisted trusted project hooks unless --dangerously-bypass-hook-trust is passed
Summary
On Codex CLI 0.144.1, noninteractive codex exec does not dispatch a project UserPromptSubmit hook that Codex app-server hooks/list reports as enabled and trusted. The same hook runs when --dangerously-bypass-hook-trust is supplied.
Reproduction
A repository has .codex/hooks.json with a UserPromptSubmit command hook that returns a blocking response. Persist trust for that project hook, then verify through app-server hooks/list:
- enabled: true
- trustStatus: trusted
- current hash matches the manifest
- no warnings/errors
From both repository root and a nested directory:
- Run codex exec without --dangerously-bypass-hook-trust, using an invalid model slug so provider traffic is easy to distinguish.
- The hook is skipped and the request reaches provider/model validation (HTTP 400).
- Repeat with --dangerously-bypass-hook-trust.
- The hook dispatches and blocks before provider traffic; the turn completes with zero input/output tokens.
Nested manifest discovery itself works: hooks/list returns the same trusted project hook key/hash from root and nested cwd.
Expected
A project hook reported as persisted rusted runs in codex exec without a dangerous trust-bypass flag. The bypass flag should remain an explicit override, not the only way to activate already-trusted hooks.
Actual
Persisted trust is ignored by noninteractive exec; the hook runs only with --dangerously-bypass-hook-trust.
Environment
- codex-cli 0.144.1
- Windows 11 / PowerShell
- Project UserPromptSubmit command hook
A privacy-preserving downstream reproduction is tracked at https://github.com/anthony-chaudhary/fak/issues/3939.
2 Comments
Not reproducing on 0.147.0 (macOS): with trust persisted and no bypass flag,
codex execdispatched both hooks I registered.Reporting a negative result, since the version has moved on from the 0.144.1 in the original report and a data point either way seems useful.
Setup
codex-cli 0.147.0, macOS 26.5.2, arm64CODEX_HOME, fresh repository, everything committed[projects."<repo>"] trust_level = "trusted"<repo>/.codex/hooks.json: aPreToolUsecommand hook onapply_patch|Write|Edit|MultiEdit, and aUserPromptSubmitcommand hook with an empty matcher, which is the event in the original reporthooks/list:trustStatus: trusted,enabled: true,source: projectRun
No
--dangerously-bypass-hook-trust.Result
Both handlers ran for real, not just in the status line. The
UserPromptSubmittracer wrote its payload file, and thePreToolUsehook'sadditionalContextarrived in the rollout as a developer message.What did reproduce
An untrusted hook is skipped in
codex execwith nothing printed at all: no warning, no mention in the run output, exit 0. That is the documented trust rule, but non-interactive mode is the one place where the prompt cannot be offered, so it is also the place where the skip is least visible. Worth a line on stderr naming the count and the reason, sincehooks/listalready computes both.A note on persisting trust, for anyone else testing this
Trust is stored under
[hooks.state], keyed exactly ashooks/listreports:Writing that entry makes a subsequent headless run start trusted, which is what let me test the second event without a second keyboard approval. Obviously only sensible against a throwaway
CODEX_HOMEand a hook you wrote yourself.Happy to re-run against a different event or a nested working directory if that would help narrow what differs from the original report.
Current-platform retest (2026-08-26): I cannot reproduce this on Codex CLI 0.149.0 on macOS 26.6.2 (arm64).
I used a fresh Git repo with a project
.codex/config.tomldeclaring a blockingUserPromptSubmitcommand hook, plus a freshCODEX_HOME. The app-server flow was:hooks/list-> project hook discovered asuntrusted, with acurrentHash.config/batchWrite-> persisted that exact hash underhooks.state.hooks/list-> the same hook reportedtrusted.codex execwithout--dangerously-bypass-hook-trust-> the hook ran and blocked before the provider request.This agrees with the earlier macOS 0.147.0 report, but does not invalidate the original Windows reproduction. I therefore do not have a justified code patch from current macOS behavior. The remaining investigation looks Windows-specific and may overlap #40247; an exact current Windows retest would be the useful next discriminator.