codex - 💡(How to fix) Fix Windows command_execution does not emit PreToolUse hooks, even with matcher "*"

Official PRs (…)
ON THIS PAGE

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

On Windows, shell commands executed by Codex CLI/Desktop appear as command_execution events and do not appear to emit PreToolUse hooks from %USERPROFILE%\.codex\hooks.json.

This still happens when the hook matcher is changed from "Bash" to "*", so this does not appear to be just an unknown matcher-string issue.

This is not specific to the third-party hook command used for the test. That command was only used as a real hook executable that demonstrably blocks when invoked directly with a Codex-style hook payload. The issue is that Codex does not appear to invoke the hook for the Windows command_execution path.

Error Message

The destructive command executes successfully even when the hook config uses matcher: "*".

Root Cause

On Windows, shell commands executed by Codex CLI/Desktop appear as command_execution events and do not appear to emit PreToolUse hooks from %USERPROFILE%\.codex\hooks.json.

This still happens when the hook matcher is changed from "Bash" to "*", so this does not appear to be just an unknown matcher-string issue.

This is not specific to the third-party hook command used for the test. That command was only used as a real hook executable that demonstrably blocks when invoked directly with a Codex-style hook payload. The issue is that Codex does not appear to invoke the hook for the Windows command_execution path.

Code Example

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "%USERPROFILE%\\.local\\bin\\dcg.exe"
          }
        ]
      }
    ]
  }
}

---

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "%USERPROFILE%\\.local\\bin\\dcg.exe"
          }
        ]
      }
    ]
  }
}

---

7B 0D 0A 20 20 20 20 22

---

$payload = '{"tool_name":"Bash","turn_id":"test-turn","tool_input":{"command":"git reset --hard HEAD~1"}}'
$payload | & "$env:USERPROFILE\.local\bin\dcg.exe"
$LASTEXITCODE

---

BLOCKED: Destructive Command Detected
Rule: core.git:reset-hard
exit code: 2

---

codex exec --ephemeral --json -s danger-full-access -C <disposable-repo> "Run exactly this shell command and then report the result: git reset --hard HEAD~1"

---

{
  "type": "item.completed",
  "item": {
    "id": "item_1",
    "type": "command_execution",
    "command": "\"C:\\\\WINDOWS\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe\" -Command 'git reset --hard HEAD~1'",
    "aggregated_output": "HEAD is now at <redacted> one\n",
    "exit_code": 0,
    "status": "completed"
  }
}
RAW_BUFFERClick to expand / collapse

Summary

On Windows, shell commands executed by Codex CLI/Desktop appear as command_execution events and do not appear to emit PreToolUse hooks from %USERPROFILE%\.codex\hooks.json.

This still happens when the hook matcher is changed from "Bash" to "*", so this does not appear to be just an unknown matcher-string issue.

This is not specific to the third-party hook command used for the test. That command was only used as a real hook executable that demonstrably blocks when invoked directly with a Codex-style hook payload. The issue is that Codex does not appear to invoke the hook for the Windows command_execution path.

Environment

  • OS: Windows
  • Shell used by Codex execution path: Windows PowerShell
  • Codex CLI: observed with Windows Codex CLI/Desktop flow
  • Hook config path: %USERPROFILE%\.codex\hooks.json
  • Sandbox mode for repro: danger-full-access

Hook config tested

First, the installer-style matcher:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "%USERPROFILE%\\.local\\bin\\dcg.exe"
          }
        ]
      }
    ]
  }
}

Then the wildcard matcher:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "%USERPROFILE%\\.local\\bin\\dcg.exe"
          }
        ]
      }
    ]
  }
}

The wildcard config was written as UTF-8 without BOM. First bytes:

7B 0D 0A 20 20 20 20 22

Direct hook invocation works

The hook executable blocks correctly when invoked directly with a Codex-style payload:

$payload = '{"tool_name":"Bash","turn_id":"test-turn","tool_input":{"command":"git reset --hard HEAD~1"}}'
$payload | & "$env:USERPROFILE\.local\bin\dcg.exe"
$LASTEXITCODE

Observed direct result:

BLOCKED: Destructive Command Detected
Rule: core.git:reset-hard
exit code: 2

So the hook executable itself is functional when it receives the expected payload.

Reproduction

Create a disposable git repository with two commits, then run Codex CLI against that disposable repo:

codex exec --ephemeral --json -s danger-full-access -C <disposable-repo> "Run exactly this shell command and then report the result: git reset --hard HEAD~1"

Observed behavior

The destructive command executes successfully even when the hook config uses matcher: "*".

Relevant JSON stream item:

{
  "type": "item.completed",
  "item": {
    "id": "item_1",
    "type": "command_execution",
    "command": "\"C:\\\\WINDOWS\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe\" -Command 'git reset --hard HEAD~1'",
    "aggregated_output": "HEAD is now at <redacted> one\n",
    "exit_code": 0,
    "status": "completed"
  }
}

There is no tool_name field in that command_execution stream item.

I also tested a matcher: "command_execution" hook entry earlier; that did not block the command either.

Expected behavior

One of these should be true:

  1. Windows command_execution should emit PreToolUse hooks, and matcher: "*" should fire before the command runs.
  2. If command_execution uses a specific matcher string, it should be documented and the event payload passed to the hook should include enough information for the hook to inspect the actual command.
  3. If Windows command_execution is not intended to be hookable, the hooks documentation/UI should clearly state that this execution path is not covered.

Related issues

This appears related in class to hook coverage gaps where some handlers do not emit PreToolUse hooks:

  • #20204
  • #20616
  • #16732

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

One of these should be true:

  1. Windows command_execution should emit PreToolUse hooks, and matcher: "*" should fire before the command runs.
  2. If command_execution uses a specific matcher string, it should be documented and the event payload passed to the hook should include enough information for the hook to inspect the actual command.
  3. If Windows command_execution is not intended to be hookable, the hooks documentation/UI should clearly state that this execution path is not covered.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING