Skip to content

Fix claude-mem codex-hooks.json for current Codex: drop description (0.140 parser) + filter SessionStart no-op#2953

Closed
shaneli2016 wants to merge 3 commits into
thedotmack:mainfrom
shaneli2016:fix/codex-sessionstart-invalid-json
Closed

Fix claude-mem codex-hooks.json for current Codex: drop description (0.140 parser) + filter SessionStart no-op#2953
shaneli2016 wants to merge 3 commits into
thedotmack:mainfrom
shaneli2016:fix/codex-sessionstart-invalid-json

Conversation

@shaneli2016

@shaneli2016 shaneli2016 commented Jun 16, 2026

Copy link
Copy Markdown

Problem

plugin/hooks/codex-hooks.json breaks on current Codex CLI in two ways:

  1. Codex 0.140 fails to even parse the file — its stricter deny_unknown_fields hooks parser rejects the top-level description key:
    failed to parse plugin hooks config .../codex-hooks.json: unknown field `description`, expected `hooks`
    
  2. On 0.139 it parses but SessionStart fails — the context hook pipes the worker's {"continue":true} (a Claude Code no-op) to Codex, which rejects it: SessionStart hook (failed): hook returned invalid session start JSON output.

Fixes (all in plugin/hooks/codex-hooks.json)

  • Remove the top-level description field so the file parses on Codex 0.140+ (only hooks remains; matcher/timeout/statusMessage are still accepted).
  • worker-start hook: append >/dev/null so its stdout isn't read as hook output.
  • context hook: pipe through a filter that only forwards a real SessionStart payload, dropping the {"continue":true} no-op:
    ... hook codex context | node -e 'let s="";process.stdin.on("data",d=>s+=d);process.stdin.on("end",()=>{s=s.trim();if(!s)return;try{const j=JSON.parse(s);if(j&&j.hookSpecificOutput&&j.hookSpecificOutput.hookEventName==="SessionStart")process.stdout.write(JSON.stringify(j));}catch(e){process.stdout.write(s);}})'
    

Preferred root fix (maintainer's call)

Cleaner would be in worker-service.cjs: when invoked as hook codex context, never emit {"continue":true} — return a valid Codex hookSpecificOutput payload or nothing.

Testing

printf 'Reply with exactly: OK\n' | codex exec --skip-git-repo-check

Verified on Codex CLI 0.140.0 + claude-mem 13.6.1: 0 parse errors, all SessionStart hooks Completed, no invalid session start JSON output.

…lid JSON

The shipped codex-hooks.json SessionStart context hook pipes the worker's
{"continue":true} (a Claude Code no-op) straight to Codex, which rejects it as
"hook returned invalid session start JSON output". Suppress the worker-start
stdout and filter the context hook to only forward a real SessionStart
hookSpecificOutput payload (empty otherwise).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes two distinct breakages in plugin/hooks/codex-hooks.json for Codex CLI 0.140+: the stricter parser rejects the top-level description key, and the SessionStart context hook was emitting a Claude Code no-op ({"continue":true}) that Codex rejects as invalid hook output.

  • Removes description from the root of the JSON so Codex 0.140's deny_unknown_fields parser accepts the file.
  • Silences worker-start stdout by appending >/dev/null, preventing its output from being read as hook output.
  • Rewrites the context hook to capture output into _CTX (propagating exit codes via || exit $?) and pipes through a Node.js inline filter that forwards only valid SessionStart payloads to Codex's stdout, redirecting anything else to stderr.

Confidence Score: 5/5

Safe to merge — all three changes are narrowly scoped compatibility fixes with no logic regressions in the hook integration paths.

The description removal is a clean one-line fix. The >/dev/null addition on worker-start is straightforward. The context hook rewrite correctly captures exit codes with _CTX=$(...) || exit $? and routes non-SessionStart or non-JSON output to stderr rather than letting it reach Codex. Both issues identified in earlier review rounds are resolved in the current code.

No files require special attention.

Important Files Changed

Filename Overview
plugin/hooks/codex-hooks.json Three targeted fixes: remove top-level description field (Codex 0.140 parse fix), append >/dev/null to worker-start stdout, and replace the context hook's direct invocation with a two-step capture+filter approach that propagates the exit code and redirects non-JSON to stderr.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Codex
    participant worker_start as worker-start hook
    participant worker_svc as worker-service.cjs
    participant ctx_hook as context hook (shell)
    participant node_filter as node inline filter

    Codex->>worker_start: SessionStart fired
    worker_start->>worker_svc: node bun-runner.js ... start
    worker_svc-->>worker_start: "stdout (>/dev/null, discarded)"
    worker_start-->>Codex: exit 0 (no output)

    Codex->>ctx_hook: SessionStart fired
    ctx_hook->>worker_svc: hook codex context
    worker_svc-->>ctx_hook: _CTX (stdout captured)
    Note over ctx_hook: _CTX=$(...) || exit $?
    ctx_hook->>node_filter: "printf '%s' "$_CTX" | node -e ..."
    alt valid SessionStart JSON
        node_filter-->>Codex: JSON.stringify(j) via stdout
    else "{continue:true} or non-SessionStart JSON"
        node_filter-->>Codex: (no stdout output)
    else non-JSON / garbage
        node_filter-->>ctx_hook: process.stderr.write(s)
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Codex
    participant worker_start as worker-start hook
    participant worker_svc as worker-service.cjs
    participant ctx_hook as context hook (shell)
    participant node_filter as node inline filter

    Codex->>worker_start: SessionStart fired
    worker_start->>worker_svc: node bun-runner.js ... start
    worker_svc-->>worker_start: "stdout (>/dev/null, discarded)"
    worker_start-->>Codex: exit 0 (no output)

    Codex->>ctx_hook: SessionStart fired
    ctx_hook->>worker_svc: hook codex context
    worker_svc-->>ctx_hook: _CTX (stdout captured)
    Note over ctx_hook: _CTX=$(...) || exit $?
    ctx_hook->>node_filter: "printf '%s' "$_CTX" | node -e ..."
    alt valid SessionStart JSON
        node_filter-->>Codex: JSON.stringify(j) via stdout
    else "{continue:true} or non-SessionStart JSON"
        node_filter-->>Codex: (no stdout output)
    else non-JSON / garbage
        node_filter-->>ctx_hook: process.stderr.write(s)
    end
Loading

Reviews (3): Last reviewed commit: "Harden context hook: propagate worker ex..." | Re-trigger Greptile

Comment thread plugin/hooks/codex-hooks.json Outdated
…known fields)

Codex CLI 0.140 deny_unknown_fields rejects the top-level "description" key:
`failed to parse plugin hooks config ... unknown field `description`, expected `hooks``.
Remove it so the hooks config parses on current Codex; only `hooks` remains.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shaneli2016 shaneli2016 changed the title Fix Codex SessionStart: stop emitting Claude-protocol no-op rejected as "invalid session start JSON output" Fix claude-mem codex-hooks.json for current Codex: drop description (0.140 parser) + filter SessionStart no-op Jun 16, 2026
Comment thread plugin/hooks/codex-hooks.json Outdated
…derr

Addresses review feedback on the SessionStart context hook:
- Capture `hook codex context` output and `|| exit $?` so a crashed/failed
  worker surfaces to Codex instead of being masked by the filter's exit 0.
- catch branch writes non-JSON to stderr instead of re-emitting it to stdout,
  so malformed output can't re-trigger 'invalid session start JSON output'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shaneli2016

Copy link
Copy Markdown
Author

Thanks for the review — both points on the context hook are valid and now addressed (latest commit):

  1. catch re-emitting non-JSON → the catch branch now writes to stderr instead of stdout, so malformed/plain-text worker output can never reach Codex's hook reader (no risk of re-triggering "invalid session start JSON output").
  2. Pipeline exit code masked → the worker call is now captured with _CTX=$(… hook codex context) || exit $? before filtering, so a crashed/failed worker surfaces its non-zero exit to Codex instead of being swallowed by the filter's exit 0.

Tested the four paths locally: no-op {"continue":true} → empty/exit 0; real SessionStart payload → forwarded; non-JSON → stderr only; worker non-zero exit → propagated.

(The simplest root fix is still worker-side — emit a valid Codex payload or nothing for hook codex context — but this keeps the shim self-contained.)

@Glucksberg

Copy link
Copy Markdown
Contributor

I validated the parser side of this on Codex CLI 0.142.0: the top-level description removal is enough to get the hooks manifest accepted again, and that same minimal fix is already isolated in #2948.

This PR still covers the extra SessionStart output-filtering behavior, so I would treat it as a follow-up after #2948 lands or rebase/drop the duplicate description commit to keep the scopes clean.

@thedotmack

Copy link
Copy Markdown
Owner

Superseded by #3127, which fixes the root cause in `worker-service.cjs` (the SessionStart no-op fallback in `hook-command.ts` now emits a valid `hookSpecificOutput` instead of leaking a bare `{"continue":true}`) rather than patching the shipped `codex-hooks.json` shell script. The top-level `description` field this PR also addressed was already dropped separately. Closing in favor of #3127.

@thedotmack thedotmack closed this Jul 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants