Feature: PostToolUseFailure hook (failure-branch sibling of PostToolUse)

Open 💬 2 comments Opened May 28, 2026 by Nubaeon
💡 Likely answer: A maintainer (etraut-openai, contributor) responded on this thread — see the highlighted reply below.

Problem

The PostToolUse hook fires only after a tool produces a successful output — run_post_tool_use_hooks is gated on success in core/src/tools/registry.rs. There is currently no hook surface for the failure branch: when a tool invocation errors, exits non-zero, times out, or returns an unsuccessful result. Hook authors who want to react to tool failures (diagnostics, automated retry guidance, failure telemetry, post-mortem capture) have no event to attach to.

Proposed design

Add a PostToolUseFailure hook event — the failure-branch sibling of PostToolUse:

  • Matcher-bearing (matches on tool name) and turn-scoped — identical dispatch semantics to PostToolUse.
  • Input mirrors the PostToolUse contract plus an error_message field describing why the invocation failed.
  • Two failure sources are covered:
  • handler error — tool identity recomputed from the pre-tool-use payload, tool_response: null;
  • unsuccessful result — identity and the partial response taken from the post-tool-use payload.
  • Fail-open: a failing hook subprocess never changes the underlying tool outcome.

This follows the established hook-event pattern (e.g. the recent SubagentStart / SubagentStop additions), so it is additive and low-risk to the rest of the hook system.

Why this is generic

Reacting to tool failures is a need independent of any particular integration — observability, retry/recovery UX, and failure analytics all want a hook here, and PostToolUse deliberately excludes the failure case. It rounds out the tool-lifecycle hook surface (PreToolUse → PostToolUse → PostToolUseFailure).

Offer

Per the contributing guidelines, I'm raising this as an issue first rather than an unsolicited PR. I have a complete, tested reference implementation against current main (enum variant, schema input/output wires + generated fixtures, dispatcher/runtime/registry wiring, unit tests; cargo fmt and clippy clean) and would be glad to submit a PR if the team feels this aligns with the roadmap and would like to invite one. Happy to iterate on the design (e.g. trimming the output contract to informational-only) based on your preferences.

View original on GitHub ↗

2 Comments

JunJieLiu51520 · 3 months ago

Architecture analysis

I traced the existing hook dispatch path to validate the proposed design. The approach is architecturally sound and fits the existing system cleanly.

Current flow (the gap)

In codex-rs/core/src/tools/registry.rs, the dispatch function:

let success = match &result {
    Ok((_, success)) => *success,
    Err(_) => false,
};
// ...
let post_tool_use_payload = if success {
    // builds PostToolUse payload...
} else {
    None   // ← both failure types fall here, PostToolUse is skipped entirely
};

Two distinct failure cases both reach None:

  • Handler error: handle_any_tool() returns Err(FunctionCallError) — no tool output exists
  • Unsuccessful result: tool ran but success_for_logging() is false (e.g. non-zero exit code) — tool output exists but is marked unsuccessful

Files that need changes for PostToolUseFailure

  1. codex-rs/protocol/src/protocol.rs (~line 1352) — add PostToolUseFailure to HookEventName enum
  2. codex-rs/hooks/src/schema.rs (~line 99) — add to HookEventNameWire; add PostToolUseFailureCommandInput struct (mirrors PostToolUseCommandInput + error: String + error_kind: String)
  3. codex-rs/hooks/src/engine/mod.rs (~line 64) — add "post-tool-use-failure" label in event_name_label(), add dispatch methods
  4. codex-rs/hooks/src/events/ — new post_tool_use_failure.rs following the pattern of post_tool_use.rs
  5. codex-rs/hooks/src/registry.rs — register preview/run methods
  6. codex-rs/core/src/hook_runtime.rs (~line 261) — add run_post_tool_use_failure_hooks() alongside run_post_tool_use_hooks()
  7. codex-rs/core/src/tools/registry.rs (~line 582) — call run_post_tool_use_failure_hooks() in the !success branch
  8. codex-rs/hooks/schema/generated/ — regenerate schema fixtures

Input contract suggestion

{
  "hook_event_name": "PostToolUseFailure",
  "tool_name": "Bash",
  "tool_input": { "command": "..." },
  "tool_use_id": "...",
  "error": "exit code 1",
  "error_kind": "unsuccessful_result"   // or "handler_error"
}

The error_kind field distinguishes the two failure cases so hook authors can filter precisely. When error_kind = "handler_error", there is no tool_response; when error_kind = "unsuccessful_result", the partial tool output is available.

Output contract

Since the tool has already failed, PostToolUseFailure does not need a decision: block semantic — it is purely observational. HookUniversalOutputWire (supporting stop/continue) is sufficient, consistent with the author's stated intent.

The overall design follows the SubagentStart/SubagentStop and PreCompact/PostCompact symmetric-event precedent and is entirely additive.

Happy to submit a PR if the team would like to invite one.

etraut-openai contributor · 3 months ago

@JunJieLiu51520, please stop spamming our issue tracker with AI-generated posts. It's generating a lot of noise, and it's not helpful.