codex exec skips persisted trusted project hooks unless --dangerously-bypass-hook-trust is passed

Open 💬 2 comments Opened Jul 11, 2026 by claude-ai-netra

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:

  1. Run codex exec without --dangerously-bypass-hook-trust, using an invalid model slug so provider traffic is easy to distinguish.
  2. The hook is skipped and the request reaches provider/model validation (HTTP 400).
  3. Repeat with --dangerously-bypass-hook-trust.
  4. 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.

View original on GitHub ↗

2 Comments

nordscope-fi · 18 days ago

Not reproducing on 0.147.0 (macOS): with trust persisted and no bypass flag, codex exec dispatched 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, arm64
  • Throwaway CODEX_HOME, fresh repository, everything committed
  • [projects."<repo>"] trust_level = "trusted"
  • Two project hooks in <repo>/.codex/hooks.json: a PreToolUse command hook on apply_patch|Write|Edit|MultiEdit, and a UserPromptSubmit command hook with an empty matcher, which is the event in the original report
  • Trust granted through the startup prompt, then verified with hooks/list: trustStatus: trusted, enabled: true, source: project

Run

codex exec --approve-for-me --skip-git-repo-check "Edit notes.md so its body reads exactly: …" < /dev/null

No --dangerously-bypass-hook-trust.

Result

hook: UserPromptSubmit
hook: UserPromptSubmit Completed
hook: PreToolUse
hook: PreToolUse Completed

Both handlers ran for real, not just in the status line. The UserPromptSubmit tracer wrote its payload file, and the PreToolUse hook's additionalContext arrived in the rollout as a developer message.

What did reproduce

An untrusted hook is skipped in codex exec with 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, since hooks/list already computes both.

A note on persisting trust, for anyone else testing this

Trust is stored under [hooks.state], keyed exactly as hooks/list reports:

[hooks.state."<sourcePath>:<event>:<group index>:<hook index>"]
trusted_hash = "sha256:…"     # the `currentHash` field from hooks/list

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_HOME and 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.

JulienJBO · 2 days ago

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.toml declaring a blocking UserPromptSubmit command hook, plus a fresh CODEX_HOME. The app-server flow was:

  1. hooks/list -> project hook discovered as untrusted, with a currentHash.
  2. config/batchWrite -> persisted that exact hash under hooks.state.
  3. A fresh hooks/list -> the same hook reported trusted.
  4. codex exec without --dangerously-bypass-hook-trust -> the hook ran and blocked before the provider request.
  5. Repeated from a nested directory in the same repo -> the trusted project hook ran again.

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.