Stop hook invalid JSON error is too opaque for unsupported fields
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:
- https://developers.openai.com/codex/hooks
Stopsection: plain text is invalid;decision/reasonare the documented continuation shape.
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-L353StopCommandOutputWirehas#[serde(deny_unknown_fields)]- fields are only the flattened universal output,
decision, andreason codex-rs/hooks/src/engine/output_parser.rs#L215-L232parse_stop()deserializes directly intoStopCommandOutputWirecodex-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:
- Surface the serde unknown-field error directly, e.g. unsupported field
hookSpecificOutputforStop. - Add event-aware validation diagnostics before returning the generic parse error.
- Optionally, if intended, support
hookSpecificOutput.additionalContextonStoptoo 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.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗