Skip to content

Review fails with JSONL parse error: bracketed paste mode escape sequence in output ([?2004h) #23

Open

Description

@Mingsicogi

Bug

/codex:review (both foreground and background) fails with a JSONL parse error when the Codex CLI outputs terminal escape sequences (bracketed paste mode) that corrupt the JSON stream.

Error

Failed to parse codex app-server JSONL: Unexpected token '', "[?2004h" is not valid JSON

Reproduction

  1. Authenticate with codex login
  2. Have a working tree with staged/unstaged changes
  3. Run /codex:review from Claude Code (either foreground or background mode)
  4. The companion script (codex-companion.mjs) attempts to parse the Codex CLI stdout as JSONL
  5. Terminal escape sequence [?2004h (bracketed paste mode) is included in the output, breaking JSON parsing

Both foreground and background execution produce the same error:

# Foreground
node "codex-companion.mjs" review
# → Failed to parse codex app-server JSONL: Unexpected token '', "[?2004h" is not valid JSON

# Background (via Claude Code Bash tool with run_in_background: true)
node "codex-companion.mjs" review
# → Same error

Root Cause

[?2004h is the ANSI escape sequence for enabling bracketed paste mode, typically emitted by shells (bash/zsh) during initialization. When the Codex CLI spawns a subprocess or the shell environment leaks these escape codes into stdout, the JSONL parser in the companion script receives non-JSON data and fails.

This likely happens because:

  • The user's shell profile (.zshrc / .bashrc) emits escape sequences on startup
  • The Codex CLI subprocess inherits the terminal settings and outputs control sequences
  • The companion script does not strip ANSI escape sequences before parsing JSONL lines

Suggested Fix

Strip ANSI escape sequences from the Codex CLI output before attempting JSONL parsing. For example:

// Before parsing each line as JSON
const ansiRegex = /\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07]*\x07/g;
const cleanLine = rawLine.replace(ansiRegex, '').trim();
if (!cleanLine) continue; // skip empty lines after stripping
const parsed = JSON.parse(cleanLine);

Environment

  • macOS 15.5 (Darwin 24.6.0)
  • Claude Code (CLI)
  • Node.js (via Claude Code Bash tool)
  • Shell: zsh
  • codex-plugin-cc: latest
  • Codex CLI: authenticated and working (codex login successful)

Additional Context

Activity

  1. added 2 commits that reference this issue on Mar 31, 2026
    08103c9
    665a042
  2. added a commit that references this issue on Apr 1, 2026
    c36ee94
  3. added a commit that references this issue on Apr 7, 2026
    5edf7ad
  4. deepak-imh commented on Apr 7, 2026

    @deepak-imh

    Fix submitted in #171.

    Root cause: Shells (zsh/bash) emit terminal control sequences like \e[?2004h (bracketed paste mode) into subprocess stdout during initialization. The JSONL protocol reader passes these directly to JSON.parse(), which fails.

    Fix: Adds a stripAnsi() utility that removes CSI and OSC escape sequences before JSON.parse() in both parsing paths (app-server.mjs handleLine + app-server-broker.mjs socket handler). 7 new tests, all 73 existing tests pass.

  5. equilibriumweb commented on Apr 14, 2026

    @equilibriumweb

    Still reproducing on plugin version 1.0.2 today. Ping on #171 — the fix is ready and this bug currently blocks /codex:review and /codex:adversarial-review end-to-end for users on zsh.

    Environment

    • Plugin: openai/codex-plugin-cc @ 1.0.2
    • Shell: /bin/zsh (default)
    • Node: v24.8.0
    • OS: macOS 26.3.1

    Reproduction

    node "~/.claude/plugins/cache/openai-codex/codex/1.0.2/scripts/codex-companion.mjs" review ""
    # → Failed to parse codex app-server JSONL: Unexpected token '', "[?2004h" is not valid JSON

    Same failure on adversarial-review subcommand. Both foreground and background modes via Claude Code's Bash tool fail identically, since both share the companion script's JSONL parser.

    Workaround today is running a manual self-review or asking Claude to do a static review — neither substitutes Codex's value.

    Would appreciate getting #171 merged and cutting a 1.0.3 release. Happy to test the fix against my repro once released.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions