fix: Codex SessionStart hook rejects no-op fallback as invalid JSON#3127
Merged
Conversation
hook-command.ts fell back to a bare {continue:true, suppressOutput:true}
whenever a hook errored before its handler ran (missing session_id,
invalid cwd, missing transcript path). Codex's strict SessionStart
validator rejects that shape outright ("hook returned invalid session
start JSON output"), since `context` is the SessionStart handler on every
platform. buildNoOpResult() now attaches a minimal valid
hookSpecificOutput for that path so Codex's startup hook no longer fails.
Closes #2947
Closes #2972
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YMT5iCF1kfikq7fetR6NF
This was referenced Jul 4, 2026
Contributor
codexAdapter.formatOutput used a truthy check on additionalContext,
silently dropping the key whenever it was an explicit empty string
(HookResult types it as a required string, not optional). The new
SessionStart no-op fallback from buildNoOpResult('context') hit this
exactly, emitting hookSpecificOutput without additionalContext at all.
Switch to a typeof check so the key is preserved whenever the caller
set it, matching the type contract.
Addresses review feedback on #3127.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YMT5iCF1kfikq7fetR6NF
adambenhassen
pushed a commit
to adambenhassen/claude-mem-privacy
that referenced
this pull request
Jul 9, 2026
codexAdapter.formatOutput used a truthy check on additionalContext,
silently dropping the key whenever it was an explicit empty string
(HookResult types it as a required string, not optional). The new
SessionStart no-op fallback from buildNoOpResult('context') hit this
exactly, emitting hookSpecificOutput without additionalContext at all.
Switch to a typeof check so the key is preserved whenever the caller
set it, matching the type contract.
Addresses review feedback on thedotmack#3127.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YMT5iCF1kfikq7fetR6NF
adambenhassen
pushed a commit
to adambenhassen/claude-mem-privacy
that referenced
this pull request
Jul 9, 2026
codexAdapter.formatOutput used a truthy check on additionalContext,
silently dropping the key whenever it was an explicit empty string
(HookResult types it as a required string, not optional). The new
SessionStart no-op fallback from buildNoOpResult('context') hit this
exactly, emitting hookSpecificOutput without additionalContext at all.
Switch to a typeof check so the key is preserved whenever the caller
set it, matching the type contract.
Addresses review feedback on thedotmack#3127.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
hook-command.tsfell back to a bare{continue:true, suppressOutput:true}whenever a hook errored before its handler ran (missingsession_id, invalidcwd, missing transcript path) — regardless of which hook event firedSessionStartvalidator rejects that bare shape as"hook returned invalid session start JSON output", since thecontexthandler is theSessionStartproducer on every platformbuildNoOpResult(event)now attaches a minimal validhookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: '' }when the fallback fires for thecontexthandler, keeping the no-op harmless everywhere elseThis is the remaining half of the bug tracked in #2972 — the
description-field parse rejection was already fixed separately, but thecontinue:trueleak on the no-op fallback path was not. Supersedes #2953 (unmerged, only patched the shippedcodex-hooks.jsonshell script rather than the root cause inworker-service.cjs).Closes #2947
Closes #2972
Test plan
bun test— 2225 pass, 0 fail (added coverage forbuildNoOpResultintests/hook-command.test.ts)tsc --noEmitcleannpm run build-and-sync— rebuilt bundle, restarted worker, confirmed fix compiled intoplugin/scripts/worker-service.cjsSessionStartpayload with nosession_idthrough the builtworker-service.cjs hook codex context— before:{"continue":true}(invalid); after:{"continue":true,"hookSpecificOutput":{"hookEventName":"SessionStart"}}(valid)🤖 Generated with Claude Code
https://claude.ai/code/session_011YMT5iCF1kfikq7fetR6NF