PostToolUse replacement is ignored for code-mode tool results

Resolved 💬 0 comments Opened Aug 4, 2026 by jbknick Closed Aug 4, 2026

Version

  • Codex CLI: 0.146.0
  • Source: current main at 3ca9f375aa2369eb901ccfc70e86c74dabc588b1
  • Platform: macOS arm64
  • Subscription/model: not relevant; this reproduces in the local tool dispatch path

What issue are you seeing?

A successful PostToolUse hook that returns continue: false plus a stopReason replaces the model-visible result for a direct tool call, but the replacement is ignored when the same tool runs inside code mode.

The hook runs once and receives the original result, yet the code-mode promise resolves with the original typed tool output. This breaks direct/code-mode parity and prevents a trusted hook from replacing a verbose result before the next model request.

Steps to reproduce

  1. Enable code mode and configure a trusted PostToolUse command hook matching Bash.
  2. Have the hook return a successful replacement using continue: false and a bounded stopReason.
  3. Run one shell command through code mode.
  4. Inspect the nested tools.exec_command result sent by code mode.

Actual: the nested result contains the original command output and omits the hook replacement.

Expected: the nested result contains the hook replacement and excludes the original output, matching the existing direct-tool behavior. Decision-block and exit-2 outcomes must continue to reject rather than resolve.

A discriminating integration test is available here:

https://github.com/jbknick/codex/commit/d90965579a8c6adfde66efafd2d26eccfafeb2fc

Root cause

PostToolUseFeedbackOutput::to_content() already delegates to model_visible, but PostToolUseFeedbackOutput::code_mode_result() delegates to original. Code mode calls result.code_mode_result(), so it bypasses the successful hook replacement.

The minimal correction is one line: delegate code_mode_result() to model_visible, exactly as the direct path does. Blocking outcomes are rejected before this wrapper is constructed, so their behavior is unchanged.

Validation

On the linked commit:

  • baseline-fail/patched-pass integration regression: pass
  • focused unit regression: pass
  • all 23 PostToolUse tests: pass
  • complete codex-core suite: 3,247 passed, 19 skipped
  • just fix -p codex-core: pass
  • just fmt: pass
  • exact-diff review: pass with no correctness or security findings

The repository-wide suite is not claimed as passed: the workspace includes codex-code-mode-runtime, whose pinned rusty_v8 150.4.0 sandbox debug archive for Apple silicon currently returns HTTP 404.

Related: #31015 and #21753.

I understand external contributions are invitation-only, so I have not opened a pull request. If this scope is acceptable, would a maintainer be willing to invite a PR for the linked minimal fix and regression?

View original on GitHub ↗