Hook failure messages should identify the failing hook and expose diagnostics

Open 💬 4 comments Opened Jun 8, 2026 by Fearvox

What problem are you seeing?

When a hook fails, Codex currently surfaces a very terse message such as:

SessionStart hook (failed)
error: hook returned invalid session start JSON output

or:

PostToolUse hook (failed)
error: hook exited with code 1

This tells the user that a hook failed, but not enough to debug it when multiple hooks are configured. In practice, the user still has to manually inspect config files and scripts to answer basic questions:

  • Which configured hook failed?
  • Where was it configured?
  • What command/script was executed?
  • What was the exit code?
  • Did stdout contain invalid JSON, mixed logs, or no output?
  • Did stderr contain a useful error message?

I hit this on Codex CLI 0.136.0 with both SessionStart and PostToolUse failures in a hooks-heavy setup. The task itself could continue, but the hook failure surface was too opaque to quickly diagnose.

Requested improvement

Please make hook failure diagnostics identify the failing hook and expose a small, safe diagnostic block.

Useful fields would be:

hook_event: PostToolUse
hook_name_or_id: <configured name/id if available>
config_source: <path or profile source, secrets redacted>
command: <script/command path, secrets redacted>
exit_code: 1
stderr_tail: <last N lines>
stdout_tail: <last N lines, if safe>
stdout_parse_error: expected JSON object, got non-JSON output / empty output / mixed log output

For JSON-producing hooks, it would be especially helpful to distinguish:

  • stdout was empty
  • stdout was invalid JSON
  • stdout contained valid JSON plus extra non-JSON logs
  • stdout had unsupported fields
  • hook exited non-zero before producing output

UI / workflow ideas

A failure message could show a compact summary with expandable details:

PostToolUse hook "my-hook-name" failed
source: ~/.codex/hooks.json
command: /path/to/script
exit_code: 1
stderr_tail: ...
stdout_parse_error: expected JSON object, received non-JSON log output

Actions that would reduce debugging friction:

  • Copy diagnostics
  • Open hook config
  • Temporarily disable this hook for this session
  • Show docs link for expected JSON schema for that hook event

Why this matters

Hooks are an advanced-user feature, but the current failure display is opaque even for advanced users. The result is that a 30-second script/config bug becomes a 10-minute hunt through hook sources.

This is also important for trust: if hooks can affect session startup or tool lifecycle behavior, users should be able to see exactly which hook failed and why.

Related issues I found before filing

This is adjacent to, but broader than:

  • #18887: Stop hook invalid JSON error is too opaque for unsupported fields
  • #22847: unable to trust hooks, error in CLI
  • #23645: Computer Use get_app_state hang and relative hook path failure are hard to diagnose
  • #26675: Plugin PostToolUse hook relative command resolves from workspace cwd

Those cover specific hook problems. This issue is about the general diagnostic surface for hook failures across hook event types.

Environment

  • Codex CLI: 0.136.0
  • Platform: macOS
  • Account/profile used to file: Fearvox

View original on GitHub ↗

4 Comments

Fearvox · 2 months ago

I implemented a candidate fix on a fork branch, but this repository currently only allows collaborators to open pull requests.

Branch:
https://github.com/Fearvox/codex/tree/hook-failure-diagnostics

Compare:
https://github.com/openai/codex/compare/main...Fearvox:hook-failure-diagnostics

Summary:

  • Adds compact hook failure diagnostics with event type, hook id/name, config source, command, exit/status, and redacted stdout/stderr tails.
  • Classifies JSON-producing hook stdout parse failures, including invalid JSON and mixed logs plus JSON.
  • Adds regression coverage for nonzero exits, invalid JSON, mixed logs + JSON, and multiple hooks where only one fails.

Validation:

  • cd codex-rs && just fmt
  • cd codex-rs && just test -p codex-hooks
  • cd codex-rs && just fix -p codex-hooks
Fearvox · 2 months ago

https://github.com/Fearvox/codex/pull/1
This is the PR in my forked repo, for better reviewing experience.

getdagnis · 2 months ago

I hope this gets fixed. Just switched to Codex CLI and pretty irritating that every response starts with three or four "SessionStart hook (failed) / error: hook exited with code 1" messages. No info what hooks it's reporting on, what it implies, how to fix them. Are those warnings or errors.

ipdisdendat · 1 day ago

Corroborating this on 0.149.1, with a correction after tracing the exact release parser.

Every user turn produced two identical failure cards:

UserPromptSubmit hook (failed)
error: hook returned invalid user prompt submit JSON output

Two separate hook registrations emitted syntactically valid JSON shaped like:

{
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "...",
    "suppressOutput": true
  }
}

In 0.149.1, UserPromptSubmitHookSpecificOutputWire rejects unknown fields and accepts only hookEventName plus optional additionalContext. suppressOutput belongs to the top-level universal output object, so an actionable diagnostic would have been:

unknown field hookSpecificOutput.suppressOutput

Moving suppressOutput to the top level makes the payload schema-valid, although the field is currently parsed but not implemented.

Instead, two different registrations collapsed to the same generic error, with neither the command nor configuration source identified. Multiple hook sources had been merged into the effective registration list, so auditing only the project hook configuration showed nothing wrong; one failing registration came from a second file outside it.

One important correction to an initially tempting diagnosis: on 0.149.1, exit 0 with empty stdout is a successful no-op, {} is valid, and plain text from UserPromptSubmit is accepted as additional context. A silent hook was therefore not the culprit. The generic error sent the investigation toward the wrong hook and cost several hours before the real registration was found.

The failing hook emitted the illegal member only when it had something to report, so it appeared healthy under a trivial test prompt.

The configuration source, command, and field-level stdout parse diagnostics proposed in this issue would have reduced this to a one-minute fix. This seems squarely within #27052; #18887 is the closely related unsupported-field case.

Minimal reproduction: configure a UserPromptSubmit hook to emit the JSON above. A command hook that exits 0 with empty stdout does not reproduce the failure.