EveryInc/compound-engineering-plugin · error · Error

${label} returned invalid JSON: ${error instanceof Error ? e

Error message

${label} returned invalid JSON: ${error instanceof Error ? error.message : String(error)}

What it means

parseJson() wraps JSON.parse of a Codex CLI command's stdout and re-raises a labeled error when the output is not valid JSON. Every consumer of `codex ... --json` output (payload(), listMarketplaces()) funnels through here so callers get a clear message instead of a raw SyntaxError.

Source

Thrown at src/dev/codex-dev.ts:438

  if (state.kind === "absent") return false
  if (state.kind === "collision") {
    throw new Error(`${context.collectionPath} exists and is not a symlink; refusing to remove it`)
  }
  if (state.kind === "unrelated") {
    throw new Error(`${context.collectionPath} points outside a Compound Engineering checkout; refusing to remove it`)
  }
  if (state.kind === "broken") {
    throw new Error(`${context.collectionPath} is a broken symlink; refusing to remove it automatically`)
  }
  await removeManagedCollectionLink(context.collectionPath, state.target)
  return true
}

function parseJson<T>(result: CommandResult, label: string): T {
  try {
    return JSON.parse(result.stdout) as T
  } catch (error) {
    throw new Error(`${label} returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`)
  }
}

async function runCodex(
  context: CodexDevContext,
  runner: CommandRunner,
  args: string[],
): Promise<CommandResult> {
  return checkedRun(runner, "codex", args, { cwd: context.repoRoot, env: context.env })
}

async function listCompoundEngineeringPlugins(
  context: CodexDevContext,
  runner: CommandRunner,
): Promise<InstalledPlugin[]> {
  const result = await runCodex(context, runner, ["plugin", "list", "--available", "--json"])
  const payload = parseJson<{ installed?: InstalledPlugin[] }>(result, "codex plugin list")
  return (payload.installed ?? []).filter(

View on GitHub (pinned to c9c10f8c75)

Solutions

  1. Run the same `codex ...` command by hand and inspect its raw stdout
  2. Update or pin the codex CLI (`codex --version`, install a stable release) to match expected JSON output
  3. Check that stdout is clean JSON — redirect stderr separately and remove shell banners/aliases
  4. Upgrade the compound-engineering CLI if the codex output format legitimately changed

Example fix

// before
$ bun run codex:dev -- status
// Error: codex plugin list returned invalid JSON: Unexpected token 'n' ...

// after
$ codex --version && codex plugin list   # see raw output
$ npm i -g @openai/codex@latest           # update CLI
$ bun run codex:dev -- status
Defensive patterns

Strategy: try-catch

Validate before calling

const out = await runCodex(context, runner, args);
if (out.exitCode !== 0 || !out.stdout.trim().startsWith("{")) {
  throw new Error(`codex output not JSON: ${out.stdout.slice(0, 200)}`);
}

Type guard

function isJsonObject(v: unknown): v is Record<string, unknown> {
  return typeof v === "object" && v !== null && !Array.isArray(v);
}

Try / catch

try {
  const plugins = await listMarketplaces(context, runner);
} catch (e) {
  if (String(e).includes("returned invalid JSON")) {
    console.error("codex CLI output unexpected — check `codex --version` and stdout for banners");
  } else throw e;
}

Prevention

When it happens

Trigger: Running a `codex` subcommand whose stdout is not parseable JSON — the CLI printed a warning/banner before the JSON, emitted human-readable output on stdout, crashed mid-print, or an unexpected codex version changed the output format.

Common situations: Outdated or prerelease codex CLI producing non-JSON output; codex writing update notices or prompts into stdout; codex not actually supporting the invoked --json flag; shell wrapper functions aliasing `codex` to something else.

Understand the failure class

Related errors


AI-assisted analysis of EveryInc/compound-engineering-plugin@c9c10f8c75 (2026-08-31). Data as JSON: /api/errors/079071d2abb46482. Report an issue: GitHub.