Windows: all Claude-marketplace plugin hooks fail — hook bash resolves to the WSL shim; async hook protocol unsupported; failures report only 'Failed' with no diagnostics
Environment
- Codex CLI 0.144.1, 0.146.0, 0.147.0 (identical behavior on all three), npm install
- Windows 11 Pro 10.0.26200
- Plugins from the
claude-plugins-officialmarketplace:security-guidance2.0.7,superpowers6.3.0,ralph-loop1.0.0 - WSL installed (Ubuntu default distro), Git for Windows installed
Summary
On Windows, every hook declared by Claude-marketplace plugins reports Failed on every fresh codex exec run — all four phases (SessionStart, UserPromptSubmit, PostToolUse, Stop). The root cause is that Codex resolves the hook interpreter bash via PATH. On a default Windows setup, PATH bash is the WindowsApps WSL shim (%LOCALAPPDATA%\Microsoft\WindowsApps\bash.exe), and WSL bash cannot execute Windows-style script paths:
$ bash "C:\Users\<user>\.codex\plugins\cache\claude-plugins-official\security-guidance\2.0.7\hooks\sg-python.sh"
/bin/bash: C:UsersAJCam.codexpluginscacheclaude-plugins-officialsecurity-guidance2.0.7hookssg-python.sh: No such file or directory
(exit 127)
Since effectively all Claude-marketplace plugin hooks are bash <script> commands, every hook fails out of the box on Windows. Claude Code runs these same hooks successfully on the same machine because it resolves bash to Git Bash rather than PATH-first.
Workaround / proof: prepending C:\Program Files\Git\bin to PATH (so bash = Git Bash) immediately fixes most phases with unmodified plugin files: UserPromptSubmit Completed, all PostToolUse Completed, security-guidance Stop Completed.
Requests
1. Resolve hook bash like Claude Code does on Windows
PATH-first resolution lands on the WSL shim and breaks every Claude-marketplace hook. Suggested: prefer Git Bash (git --exec-path discovery or the standard install locations) for bash hook commands on Windows, falling back to PATH.
2. Support (or gracefully degrade) the async hook protocol
security-guidance's SessionStart hook (ensure_agent_sdk.py) replies with Claude's async hook protocol:
{"async": true, "asyncTimeout": 180000}
and exits 0. Codex marks the phase Failed. Expected: either honor the async contract or treat a well-formed async reply as success.
3. Hook failures surface as the bare word Failed with no diagnostics
- The transcript prints only
hook: <Phase> Failed. RUST_LOG=tracesurfaces exec-policy and websocket telemetry but no hook error reason.- The app log DB records
hook/started/hook/completedevents with no failure detail. - Additionally, a hook whose
hooks.jsonhash is not in the trusted set is silently skipped with no output at all, which is indistinguishable from "did not fire" and cost us several rounds of misdiagnosis (an edited hook file gets a new hash and simply vanishes from the run).
Expected: print or log the exit code / stderr / spawn error of a failed hook, and indicate when a hook was skipped as untrusted.
4. Remaining divergence (unresolved)
ralph-loop's Stop hook (stop-hook.sh) exits 0 when run by hand under Git Bash with CLAUDE_PLUGIN_ROOT set and JSON on stdin, yet still reports Failed inside Codex's hook runtime even after the bash fix. Without failure diagnostics (point 3) we could not determine why; likely stdin/env shape divergence from Claude's hook contract.
Reproduction
- Windows machine with WSL installed (so PATH
bash= WSL shim) and anyclaude-plugins-officialplugin with hooks installed and trusted (e.g.security-guidance). codex execany prompt in any workspace.- Observe
hook: SessionStart Failed,hook: UserPromptSubmit Failed,hook: PostToolUse Failed(per tool call),hook: Stop Failed. - Prepend Git Bash to PATH and re-run:
UserPromptSubmit/PostToolUse/security-guidanceStopcomplete;SessionStartstill fails on the async reply (point 2).
2 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Confirmed on
main@ 1f41cc5d92: hook commands on Windows run throughcmd.exe /C "<command_line>"with no interpreter special-casing —build_command/default_shell_commandincodex-rs/hooks/src/engine/command_runner.rs#L372-L420. Sobash <script>resolves via cmd's PATH search, which on stock Windows lands on the WindowsApps WSL shim, and WSL bash mangles the Windows path exactly as your exit-127 capture shows. There is no Git-Bash discovery anywhere in the hooks crate.Your request 1 is the right fix and cheap: when a hook command's program is
bashon Windows, prefer Git Bash (viagit --exec-path-relative discovery or the standardGit\bin\bash.exelocations), falling back to PATH — mirroring what Claude Code does on the same machine, per your test. For request 3 (diagnostics): the runner already capturesexit_code/stdout/stderrinHandlerRunResult(#L360-L369); surfacing the exit code and a stderr tail wherever "Failed" is rendered would have made this self-diagnosing instead of requiring your shim forensics.