Code Mode / functions.exec does not fire PostToolUse hooks for nested shell results
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.5k
- PR merge metrics
- PR metrics pending
Description
Authorship disclosure: This issue was investigated and written by OpenAI Codex on behalf of @tohuw, who reviewed and requested its publication.
Important bit
A configured, visible, explicitly approved, and trusted PostToolUse hook does not fire for shell execution nested under Code Mode / functions.exec in Codex Desktop on Windows. The failure is silent: a deliberately compressible 7,634-token tool result was delivered unchanged, while invoking the same hook directly reduced an equivalent 30,191-character payload to 245 characters and returned a valid decision: "block" response.
This leaves integrations unable to observe or replace results from the execution path Codex actually selected, even though /hooks shows the hook and Codex records its trusted hash.
Environment
- Windows
- Codex Desktop app build
26.803.81509 - Bundled Codex CLI
0.147.0 - Global
hooks.jsonwith[features].hooks = true - Hook approved through
/hooks
Reproduction
Configure a PostToolUse command hook in ~/.codex/hooks.json. During diagnosis, the matcher was broadened to cover every observed/likely shell name:
{
"hooks": {
"PostToolUse": [
{
"matcher": "^(Bash|shell_command|exec_command|functions\\.exec)$",
"hooks": [
{
"type": "command",
"command": "pwsh.exe -NoProfile -File C:/Users/USER/.local/hooks/compress-result.ps1"
}
]
}
]
}
}
Restart Codex, run /hooks, and approve the hook. Confirm trust is persisted:
[hooks.state.'C:\Users\USER\.codex\hooks.json:post_tool_use:0:0']
trusted_hash = "sha256:..."
Then ask Codex to run this through its shell tool:
(1..300) | ForEach-Object {
@{id=$_; status='ok'; message='completed successfully'; duration_ms=12; repeated='same-value'} |
ConvertTo-Json -Compress
}
Actual behavior
The live tool result returned all 303 lines and was truncated by the client only after reaching 7,634 tokens:
Warning: truncated output (original token count: 7634)
Total output lines: 303
No replacement summary appeared and there was no hook failure diagnostic.
The test was repeated after each matcher/configuration change, with a full Codex restart and renewed /hooks approval. The persisted trusted hash and parsed hook configuration were verified after restart.
Control test
The hook executable and its downstream compressor were exercised directly with an equivalent PostToolUse event envelope:
HOOK_RESULT=block
original_chars=30191
compressed_chars=245
The returned JSON used the currently accepted blocking shape:
{
"decision": "block",
"reason": "<compressed result>",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Tool output compressed before the model read it."
}
}
This isolates the failure to Codex event dispatch/coverage rather than hook trust, JSON validity, process launch, or the compressor.
Expected behavior
Code Mode execution and its nested tool calls should emit symmetric PreToolUse and PostToolUse events. A trusted PostToolUse hook matching the execution surface should receive the completed result and have its documented decision applied.
At minimum, Codex should report that this execution path does not support PostToolUse; silently accepting and trusting a hook that can never fire makes integrations appear active when they are not.
Related issues
- #23411 covers Code Mode
execnot firingPreToolUse; its proposed fix explicitly leavesPostToolUseout of scope. This report is the symmetric result-side gap. - #31015 covers replacement/redaction semantics after
PostToolUsefires. Here, the hook never fires at all. - #20204 tracks inconsistent hook coverage across other handlers.
- Downstream integration report with the original discovery and measurements: https://github.com/3rg0n/thlibo/issues/126
Musings / possible fix direction
The behavior is consistent with Code Mode's outer exec handler and/or nested dispatch path lacking a PostToolUse payload implementation, analogous to the missing PreToolUse implementation identified in #23411.
Potentially useful acceptance coverage:
- Register a trusted
PostToolUsehook for Code Modeexecand nested shell calls. - Execute a nested tool returning a known marker.
- Assert the hook receives the completed payload.
- Return a valid replacement/block result.
- Assert the model-facing tool result uses the hook response rather than the original output.
I am available to test a diagnostic or patched Windows build against the downstream thlibo integration.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing Code Mode's functions.exec handler and nested tool dispatch, comparing their hook flow with the missing PreToolUse path discussed in issue #23411. Add coverage for a trusted PostToolUse hook receiving a nested shell result and returning a replacement or block response. Done means the hook fires for the nested execution path, its response reaches the model-facing result, or the unsupported path reports a clear diagnostic.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100