Yeachan-Heo/oh-my-codex · error
${source} must be valid JSON: ${(error as Error).message}
Error message
${source} must be valid JSON: ${(error as Error).message} What it means
The state command's JSON input (from --input or stdin) failed JSON.parse. The message includes the parser's position info and, for --input on Windows-like shells where quotes get stripped, an extra hint may be appended.
Source
Thrown at src/cli/state.ts:59
function looksLikeQuoteStrippedJson(raw: string): boolean {
const trimmed = raw.trim();
return trimmed.startsWith('{') && trimmed.endsWith('}') && !trimmed.includes('"');
}
function parseStateInputJson(
raw: string,
source: '--input' | '--input-file',
): Record<string, unknown> {
const json = source === '--input-file' && raw.startsWith('\uFEFF') ? raw.slice(1) : raw;
let parsed: unknown;
try {
parsed = JSON.parse(json);
} catch (error) {
let message = `${source} must be valid JSON: ${(error as Error).message}`;
if (source === '--input' && looksLikeQuoteStrippedJson(raw)) {
message += WINDOWS_QUOTE_HINT;
}
throw new Error(message);
}
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error(`${source} must decode to a JSON object`);
}
return { ...(parsed as Record<string, unknown>) };
}
export async function stateCommand(
args: string[],
deps: StateCommandDependencies = {},
): Promise<void> {
const stdout = deps.stdout ?? ((line: string) => console.log(line));
const stderr = deps.stderr ?? ((line: string) => console.error(line));
const execute = deps.execute ?? executeStateOperation;
const subcommand = args[0];
if (!subcommand || isHelpArg(subcommand)) {
stdout(STATE_HELP);View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Validate with a JSON linter or `echo "$JSON" | jq .` before passing
- On Windows, use the --input-file option with a properly quoted JSON file instead of inline --input
- Use jq -c to construct compact, correctly quoted JSON
Example fix
# before (cmd.exe strips quotes)
state set --input "{\"foo\": 1}"
# after
echo '{"foo": 1}' > state.json
state set --input-file state.json Defensive patterns
Strategy: validation
Validate before calling
function assertJson(text: string, source: string) {
try { JSON.parse(text); } catch (e) {
throw new Error(`${source} is not valid JSON: ${(e as Error).message}`);
}
}
assertJson(json, 'payload'); Try / catch
try { JSON.parse(input); }
catch (e) { /* show e.message position, fix quotes/commas */ } Prevention
- Build JSON with jq -c rather than string concatenation
- On Windows, prefer --input-file over inline --input
When it happens
Trigger: `state set --input '{key: value}'` (unquoted keys), or `--input "{"a":1}"` on a shell that strips the inner quotes, yielding invalid JSON like {a:1}.
Common situations: Windows cmd/PowerShell mangling double quotes; single-quoted heredocs with trailing commas; truncation by terminal paste limits.
Related errors
- autoresearch candidate artifact must be valid JSON
- Invalid --input JSON: ${error instanceof Error ? error.messa
- ${source} must decode to a JSON object
- Missing JSON value after --input
- Missing path value after --input-file
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/8799b8e67347708b.
Report an issue: GitHub.