codex - ✅(Solved) Fix Stop hook invalid JSON error is too opaque for unsupported fields [2 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openai/codex#18887Fetched 2026-04-22 07:51:04
View on GitHub
Comments
0
Participants
1
Timeline
8
Reactions
0
Author
Participants
Timeline (top)
labeled ×3cross-referenced ×2referenced ×2unlabeled ×1

When a Stop hook returns JSON with an unsupported field such as hookSpecificOutput, Codex reports only a generic parse failure like:

hook returned invalid stop hook JSON output

That made a real integration failure much harder to diagnose than it needed to be. The hook was returning a shape that is accepted on other events (SessionStart, UserPromptSubmit, PostToolUse) but not on Stop.

Error Message

In a multi-event hook integration, it is natural to reuse the same output builder across events. If one event has stricter schema rules than the others, a field-level diagnostic is much more actionable than a generic JSON-invalid error. At minimum, please make the error specific enough to identify the actual contract violation, for example by reporting the unexpected field name/path instead of only saying the whole payload is invalid.

  1. Surface the serde unknown-field error directly, e.g. unsupported field hookSpecificOutput for Stop.
  2. Add event-aware validation diagnostics before returning the generic parse error. I found this while debugging a Codex-only Stop hook failure in a cross-tool bridge. The local integration bug was easy to fix once I traced the schema, but that took longer than it should have because the runtime error did not reveal that the JSON was structurally valid and only had an unsupported field.

Root Cause

In a multi-event hook integration, it is natural to reuse the same output builder across events. If one event has stricter schema rules than the others, a field-level diagnostic is much more actionable than a generic JSON-invalid error.

In my case, the hook JSON itself was valid JSON and the hook exited 0; the problem was only that Stop rejected an extra field that other hook events commonly use.

Fix Action

Fixed

PR fix notes

PR #211: hooks/codex-stop: drop unsupported hookSpecificOutput (fix codex Stop error)

Description (problem / solution / changelog)

Summary

Codex agents were logging hook returned invalid stop hook JSON output after every Stop and dropping the Agent Bridge queue-context we inject there. The queue pending-count hook was effectively broken for every Codex session.

Root cause

hooks/check_inbox.py in --format codex was emitting the Claude-shaped Stop payload:

{
  "decision": "block",
  "reason": "...",
  "hookSpecificOutput": {
    "hookEventName": "Stop",
    "additionalContext": "..."
  }
}

Codex's StopCommandOutputWire uses #[serde(deny_unknown_fields)]:

The Codex Stop schema only defines decision / reason on the top-level wire — there is no hookSpecificOutput. The JSON parses fine but deny-unknown-fields rejects the whole object and Codex falls back to the generic invalid stop hook JSON output message with no hint at which field was at fault.

Fix

hooks/check_inbox.py — when --format codex, emit only decision + reason. The existing codex_stop_reason() helper already rolls the inbox-summary context into the reason string, so the user-visible signal does not regress.

scripts/smoke-test.sh — new regression guard on the codex stop-hook smoke asserting the output does NOT contain "hookSpecificOutput". Prevents silent reintroduction of the Claude-shaped payload.

Test plan

  • python3 -m py_compile hooks/check_inbox.py — PASS
  • bash -n scripts/smoke-test.sh — PASS
  • shellcheck scripts/smoke-test.sh — PASS
  • Manual: output keys are exactly ['decision', 'reason'] after the change.
  • Full ./scripts/smoke-test.sh run was started but a pre-existing unrelated hang in the late integration stage prevented full completion. The codex-stop section of smoke runs before that hang and passes.

Upstream

Upstream diagnostic request filed at openai/codex#18887 — asks Codex to surface which unsupported field tripped deny_unknown_fields instead of the opaque invalid stop hook JSON output.

Impact

Every Codex session gets the queue-context block restored on Stop. No behavior change for Claude (the Claude Stop path is untouched; the dropped block was only ever reachable when --format codex was passed).

Changed files

  • hooks/check_inbox.py (modified, +0/-4)
  • scripts/smoke-test.sh (modified, +1/-0)

PR #212: release: bump version to 0.6.5

Description (problem / solution / changelog)

Hotfix release — single PR on top of v0.6.4.

PR in this release

  • #211hooks/codex-stop: drop unsupported hookSpecificOutput from the Codex Stop payload. Codex's StopCommandOutputWire uses #[serde(deny_unknown_fields)] and the Claude-shaped field caused every Codex Stop to log hook returned invalid stop hook JSON output and drop queue-context injection. Fix emits only decision + reason; smoke gets a regression guard asserting hookSpecificOutput is absent. Upstream diagnostic-improvement request: openai/codex#18887.

v0.6.4 → v0.6.5 (patch per semver — single bug fix, no behavior change for Claude agents).

Changed files

  • VERSION (modified, +1/-1)

Code Example

{
  "decision": "block",
  "reason": "Run one more pass.",
  "hookSpecificOutput": {
    "hookEventName": "Stop",
    "additionalContext": "Queue DB is source of truth."
  }
}
RAW_BUFFERClick to expand / collapse

Summary

When a Stop hook returns JSON with an unsupported field such as hookSpecificOutput, Codex reports only a generic parse failure like:

hook returned invalid stop hook JSON output

That made a real integration failure much harder to diagnose than it needed to be. The hook was returning a shape that is accepted on other events (SessionStart, UserPromptSubmit, PostToolUse) but not on Stop.

Why this matters

In a multi-event hook integration, it is natural to reuse the same output builder across events. If one event has stricter schema rules than the others, a field-level diagnostic is much more actionable than a generic JSON-invalid error.

In my case, the hook JSON itself was valid JSON and the hook exited 0; the problem was only that Stop rejected an extra field that other hook events commonly use.

Current documented behavior

The public hooks docs say Stop expects JSON on stdout and that it supports the common output fields. The docs do not show hookSpecificOutput for Stop:

Current source behavior

At current main (6f6997758a6936b522c046191ae3cae4293e23e9 when I checked), the hooks schema for Stop is strict and does not include hookSpecificOutput:

  • codex-rs/hooks/src/schema.rs#L340-L353
    • StopCommandOutputWire has #[serde(deny_unknown_fields)]
    • fields are only the flattened universal output, decision, and reason
  • codex-rs/hooks/src/engine/output_parser.rs#L215-L232
    • parse_stop() deserializes directly into StopCommandOutputWire
  • codex-rs/hooks/src/events/stop.rs
    • when parsing fails, Codex surfaces hook returned invalid stop hook JSON output

Minimal example

This is valid JSON, but because it includes hookSpecificOutput, it is rejected by Stop:

{
  "decision": "block",
  "reason": "Run one more pass.",
  "hookSpecificOutput": {
    "hookEventName": "Stop",
    "additionalContext": "Queue DB is source of truth."
  }
}

Requested improvement

At minimum, please make the error specific enough to identify the actual contract violation, for example by reporting the unexpected field name/path instead of only saying the whole payload is invalid.

Any of these would help:

  1. Surface the serde unknown-field error directly, e.g. unsupported field hookSpecificOutput for Stop.
  2. Add event-aware validation diagnostics before returning the generic parse error.
  3. Optionally, if intended, support hookSpecificOutput.additionalContext on Stop too for parity with other events.

I am not claiming the current parser is wrong per the documented schema. The issue is that the runtime diagnostic is too opaque for a common integration mistake.

Environment

  • Codex CLI version: 0.122.0
  • Platform: macOS
  • Hooks feature enabled with features.codex_hooks=true

Additional context

I found this while debugging a Codex-only Stop hook failure in a cross-tool bridge. The local integration bug was easy to fix once I traced the schema, but that took longer than it should have because the runtime error did not reveal that the JSON was structurally valid and only had an unsupported field.

extent analysis

TL;DR

To resolve the issue, modify the Stop hook to remove the unsupported hookSpecificOutput field from its JSON output or update the Codex parser to provide more specific error messages for unknown fields.

Guidance

  • Review the Stop hook's JSON output to ensure it only includes supported fields, such as decision and reason.
  • Consider adding event-aware validation diagnostics to the Codex parser to provide more informative error messages for unknown fields.
  • If the hookSpecificOutput field is necessary for the integration, explore adding support for it in the Stop event, similar to other events like SessionStart and UserPromptSubmit.
  • Verify the fix by testing the Stop hook with the modified JSON output and checking for specific error messages when unknown fields are present.

Example

{
  "decision": "block",
  "reason": "Run one more pass."
}

This example shows the modified JSON output without the unsupported hookSpecificOutput field.

Notes

The current implementation of the Codex parser is correct according to the documented schema, but the lack of specific error messages for unknown fields makes it difficult to diagnose integration issues. Updating the parser to provide more informative error messages or modifying the Stop hook to conform to the supported fields can resolve the issue.

Recommendation

Apply a workaround by modifying the Stop hook to remove the unsupported hookSpecificOutput field from its JSON output, as this is the most straightforward solution to resolve the issue.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

codex - ✅(Solved) Fix Stop hook invalid JSON error is too opaque for unsupported fields [2 pull requests, 1 participants]