Function-call apply_patch does not route through apply_patch handler/hooks

Open 💬 4 comments Opened Jun 6, 2026 by trotsky1997
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

When apply_patch arrives as a Responses function_call rather than a custom_tool_call, Codex does not route it through the existing apply_patch runtime contract. This means hook behavior can diverge from the freeform/custom apply_patch path.

The failing shape is a function call named apply_patch, including the functions. namespace form:

{
  "type": "function_call",
  "namespace": "functions.",
  "name": "apply_patch",
  "arguments": "*** Begin Patch\n*** Add File: file.txt\n+patched\n*** End Patch",
  "call_id": "call-id"
}

Expected

The call should route to the existing apply_patch handler so that:

  • the patch is applied through the same verification/runtime path as custom/freeform apply_patch
  • PreToolUse/PostToolUse hooks see tool_name: apply_patch
  • function-call payloads still produce function_call_output, preserving the original wire shape

Observed

A regression test that emits functions.apply_patch as a function_call fails on the current implementation because the PostToolUse additional context is not injected into the follow-up request.

Candidate fix

I pushed a tested branch here:

https://github.com/trotsky1997/codex/compare/main...trotsky1997:fix-apply-patch-hooks

Commit: 5efd838408df9e5c0aa4d8897c4eb404eb80cf06

The patch:

  • canonicalizes apply_patch function calls in the router to the existing plain apply_patch tool name
  • allows the apply_patch handler to consume ToolPayload::Function while preserving function-call output responses
  • supports both raw patch arguments and JSON { "command": "..." } arguments for hook-facing command extraction
  • adds router, handler, and suite-level hook coverage

I attempted to open a draft PR, but both gh pr create and the GitHub connector were denied by repository permissions.

Verification

Red/green verified in a WSL scratch checkout: the new suite test fails on the old implementation because PostToolUse context is not injected, then passes with the candidate fix.

Passing commands:

cargo test -p codex-core --lib build_tool_call_canonicalizes_apply_patch_function_calls -- --nocapture
cargo test -p codex-core --lib pre_tool_use_payload_ -- --nocapture
RUST_MIN_STACK=8388608 cargo test -p codex-core --test all post_tool_use_records_additional_context_for_apply_patch_function_call -- --nocapture
RUST_MIN_STACK=8388608 cargo test -p codex-core --test all post_tool_use_records_additional_context_for_apply_patch -- --nocapture
RUST_MIN_STACK=8388608 cargo test -p codex-core --test all pre_tool_use_blocks_apply_patch_before_execution -- --nocapture
cargo test -p codex-core --lib function_tools_expose_default_hook_payloads_and_rewrites -- --nocapture
cargo test -p codex-core --lib spawn_agent_function_tools_use_agent_matcher_alias -- --nocapture
cargo fmt -p codex-core -- --check
git diff --check

View original on GitHub ↗

4 Comments

github-actions[bot] contributor · 2 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #26729

Powered by Codex Action

trotsky1997 · 2 months ago

Candidate fix branch / compare URL:

https://github.com/openai/codex/compare/main...trotsky1997:fix-apply-patch-hooks

I attempted to open this as a draft PR, but both gh pr create and the GitHub connector were denied by repository permissions (CreatePullRequest / Resource not accessible by integration). So I cannot submit the PR directly from this environment, but the branch is pushed and ready for maintainers to inspect or open as a PR.

abhinav-oai contributor · 1 month ago

Closing as not reproducible. Function-style apply_patch was intentionally removed; apply_patch is now exclusively a freeform custom_tool_call. The proposed regression test constructs an unsupported function_call shape, so it does not represent a supported execution path. If a real session produces this shape, please share the corresponding rollout entry and CLI version/build.

DominiqueDDR · 1 month ago

I can reproduce the same gap from a current Codex Desktop function-call surface.

Environment:

  • Codex CLI visible to the session: codex-cli 0.144.6
  • Surface: Codex in the ChatGPT desktop app / local function tool call, exposed as functions.apply_patch
  • Platform: macOS 15.3.2, arm64
  • Hooks feature: codex features list reports hooks stable true

Hook setup:

  • User-level ~/.codex/hooks.json
  • PreToolUse matcher includes Write|Edit|apply_patch|functions.apply_patch|Bash|exec_command|functions.exec_command
  • The hook command exits non-zero for broad patch transactions, including a patch that touches more than one file.

Control proving hooks are active for exec in the same session:

  • A functions.exec_command smoke containing a rich inline node -e payload was blocked with the expected guard message (RICH_INLINE_PAYLOAD_BLOCKED).
  • A heredoc/substitution smoke through functions.exec_command was also blocked.

Failing case:

  • A functions.apply_patch call adding two temporary files in one patch succeeded.
  • The same two-file patch shape was repeated with another pair of temporary files and succeeded again.
  • The expected PreToolUse block did not happen; no guard message surfaced before the files were written.

Expected:

  • The functions.apply_patch call should canonicalize to the existing apply_patch handler/hook path so PreToolUse sees a matchable apply_patch-style tool name and can block before writing.

Why this still matters:

  • The current public docs say apply_patch supports PreToolUse/PostToolUse and can be matched as apply_patch, Edit, or Write.
  • Older issue #16732 appears closed/fixed for the normal apply_patch path, but the Desktop/function-call path still behaves as a bypass in this environment.