codex - 💡(How to fix) Fix Claude Code stop-review gate returns empty output when finalMessage is missing [1 comments, 2 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#19159Fetched 2026-04-24 05:59:37
View on GitHub
Comments
1
Participants
2
Timeline
6
Reactions
0
Author
Timeline (top)
labeled ×4closed ×1commented ×1

The Codex Claude Code stop-review gate can return a nested payload with status: 1 and rawOutput: "", which causes Claude Code to block with a generic "no final output" message and no actionable diagnosis.

Observed stop-hook failure surface:

{ "status": 1, "rawOutput": "" }

This appears to happen when the underlying Codex app-server turn completes without a string finalMessage, and the plugin turns that into an empty string instead of a diagnostic.

Error Message

const rawOutput = typeof result.finalMessage === "string" && result.finalMessage.trim() !== "" ? result.finalMessage : (result.error?.message ? CODEX_TURN_ERROR: ${result.error.message} : result.reasoningSummary ? CODEX_NO_FINAL_MESSAGE (reasoning only): ${result.reasoningSummary} : result.stderr ? CODEX_TURN_STDERR: ${result.stderr} : "CODEX_TURN_NO_OUTPUT");

Root Cause

Likely root cause

Fix Action

Fix / Workaround

This does not look like a NanoClaw bug or a repo-local issue. The failure lives entirely inside the Codex Claude Code plugin path, so it seems best fixed upstream instead of patched in plugin cache.

Code Example

{ "status": 1, "rawOutput": "" }

---

~/.claude/plugins/cache/openai-codex/codex/1.0.2/scripts/
  stop-review-gate-hook.mjs
  codex-companion.mjs

---

const rawOutput = typeof result.finalMessage === "string" ? result.finalMessage : "";

---

const rawOutput = typeof result.finalMessage === "string" && result.finalMessage.trim() !== ""
  ? result.finalMessage
  : (result.error?.message
      ? `CODEX_TURN_ERROR: ${result.error.message}`
      : result.reasoningSummary
        ? `CODEX_NO_FINAL_MESSAGE (reasoning only): ${result.reasoningSummary}`
        : result.stderr
          ? `CODEX_TURN_STDERR: ${result.stderr}`
          : "CODEX_TURN_NO_OUTPUT");
RAW_BUFFERClick to expand / collapse

Summary

The Codex Claude Code stop-review gate can return a nested payload with status: 1 and rawOutput: "", which causes Claude Code to block with a generic "no final output" message and no actionable diagnosis.

Observed stop-hook failure surface:

{ "status": 1, "rawOutput": "" }

This appears to happen when the underlying Codex app-server turn completes without a string finalMessage, and the plugin turns that into an empty string instead of a diagnostic.

Symptom

In a Claude Code stop-hook flow, the first Codex review run returned a real finding. A later run returned no actionable content and produced a block decision equivalent to:

The stop-time Codex review task returned no final output. Run /codex:review --wait manually or bypass the gate.

That gives the operator no clue whether the turn failed, produced reasoning-only output, hit a rate limit, or returned malformed structured data.

Diagnosis

The problem appears to be in the Claude Code plugin cache path:

~/.claude/plugins/cache/openai-codex/codex/1.0.2/scripts/
  stop-review-gate-hook.mjs
  codex-companion.mjs

From reading the scripts:

  1. stop-review-gate-hook.mjs runs codex-companion.mjs task --json <prompt>.
  2. The hook reads the subprocess JSON payload and parses payload.rawOutput.
  3. In codex-companion.mjs, executeTaskRun() sets:
const rawOutput = typeof result.finalMessage === "string" ? result.finalMessage : "";
  1. If runAppServerTurn() returns a result with finalMessage: undefined or any non-string value, rawOutput becomes "".
  2. The subprocess still exits cleanly, so the outer stop-hook sees a successful process run but an empty nested result.
  3. The stop-hook then converts empty rawOutput into a generic blocking decision.

Why this is bad

This collapses several distinct failure modes into the same empty-output case:

  • turn completed with reasoning items only
  • nested Codex turn failed and exposed a non-zero status internally
  • app-server returned an error but no final text message
  • stderr or structured diagnostics existed but were not surfaced

The operator gets a hard block with no remediation details.

Likely root cause

The most likely combination is:

  1. runAppServerTurn() returned without a string finalMessage
  2. the nested status or error fields were not promoted into rawOutput

So the plugin loses the real reason before the stop-hook decides what to do.

Suggested fix

In codex-companion.mjs, harden the fallback path so an empty finalMessage becomes an explicit diagnostic instead of "".

Something along these lines:

const rawOutput = typeof result.finalMessage === "string" && result.finalMessage.trim() !== ""
  ? result.finalMessage
  : (result.error?.message
      ? `CODEX_TURN_ERROR: ${result.error.message}`
      : result.reasoningSummary
        ? `CODEX_NO_FINAL_MESSAGE (reasoning only): ${result.reasoningSummary}`
        : result.stderr
          ? `CODEX_TURN_STDERR: ${result.stderr}`
          : "CODEX_TURN_NO_OUTPUT");

Then in stop-review-gate-hook.mjs, treat those CODEX_* markers as diagnostics and degrade to a warning or explicit inconclusive state rather than a generic block with no context.

Extra note

This does not look like a NanoClaw bug or a repo-local issue. The failure lives entirely inside the Codex Claude Code plugin path, so it seems best fixed upstream instead of patched in plugin cache.

Environment

  • Claude Code stop-hook flow
  • Codex Claude Code plugin cache path observed locally at ~/.claude/plugins/cache/openai-codex/codex/1.0.2/
  • Nested task mode invoking codex-companion.mjs task --json ...

If useful, I can also provide a more exact repro transcript from the stop-hook flow.

extent analysis

TL;DR

Modify the codex-companion.mjs file to handle empty finalMessage cases by providing explicit diagnostic messages instead of an empty string.

Guidance

  • Identify the codex-companion.mjs file in the plugin cache path and update the rawOutput assignment to include diagnostic messages for non-string finalMessage values.
  • Treat specific diagnostic markers (e.g., CODEX_TURN_ERROR, CODEX_NO_FINAL_MESSAGE) in stop-review-gate-hook.mjs as warnings or inconclusive states instead of blocking with a generic message.
  • Verify the changes by running the Claude Code stop-hook flow and checking for improved diagnostic output in cases where finalMessage is empty or non-string.
  • Consider providing a more exact repro transcript from the stop-hook flow to aid in debugging and testing the fix.

Example

The suggested fix in codex-companion.mjs:

const rawOutput = typeof result.finalMessage === "string" && result.finalMessage.trim() !== ""
  ? result.finalMessage
  : (result.error?.message
      ? `CODEX_TURN_ERROR: ${result.error.message}`
      : result.reasoningSummary
        ? `CODEX_NO_FINAL_MESSAGE (reasoning only): ${result.reasoningSummary}`
        : result.stderr
          ? `CODEX_TURN_STDERR: ${result.stderr}`
          : "CODEX_TURN_NO_OUTPUT");

Notes

The fix should be applied upstream in the Codex Claude Code plugin, as the issue appears to be specific to the plugin's handling of empty finalMessage values.

Recommendation

Apply the suggested fix in codex-companion.mjs to provide more informative diagnostic messages and improve the handling of empty finalMessage cases. This should help operators diagnose and remediate issues more effectively.

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