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
- Run the same `codex ...` command by hand and inspect its raw stdout
- Update or pin the codex CLI (`codex --version`, install a stable release) to match expected JSON output
- Check that stdout is clean JSON — redirect stderr separately and remove shell banners/aliases
- 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
- Pin a stable codex CLI version and update deliberately
- Do not wrap `codex` in shell functions that print to stdout
- Check stderr vs stdout separation when debugging CLI output
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Cleanup currently supports only the compound-engineering plu
- Unknown cleanup target: ${target}. Use one of: ${cleanupTarg
- Local plugin path not found: ${directPath}
- Unknown bundled plugin: ${input}
- Unknown permissions mode: ${permissions}
AI-assisted analysis of EveryInc/compound-engineering-plugin@c9c10f8c75 (2026-08-31).
Data as JSON: /api/errors/079071d2abb46482.
Report an issue: GitHub.