openai / openai/codex

Code Mode / functions.exec does not fire PostToolUse hooks for nested shell results

Open
#38,850 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

app bug hooks tool-calls windows-os
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.json with [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 exec not firing PreToolUse; its proposed fix explicitly leaves PostToolUse out of scope. This report is the symmetric result-side gap.
  • #31015 covers replacement/redaction semantics after PostToolUse fires. 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:

  1. Register a trusted PostToolUse hook for Code Mode exec and nested shell calls.
  2. Execute a nested tool returning a known marker.
  3. Assert the hook receives the completed payload.
  4. Return a valid replacement/block result.
  5. 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.