nexu-io/open-design · error · Error
existing config at ${where} is not valid JSON: ${err instanc
Error message
existing config at ${where} is not valid JSON: ${err instanceof Error ? err.message : String(err)} What it means
Thrown by parseJsonObject() in the MCP agent install pipeline when the existing target config file (e.g. claude_desktop_config.json, ~/.codex/config.json) exists but JSON.parse() throws a SyntaxError. The install pipeline refuses to overwrite a config it cannot parse, to avoid silently destroying a user's hand-maintained configuration. The error message includes the file path (`where`) and the underlying parse error.
Source
Thrown at apps/daemon/src/mcp-agent-install.ts:421
for (const key of plan.keyPath) {
const next = cursor[key];
if (next == null || typeof next !== 'object' || Array.isArray(next)) {
return null;
}
cursor = next as Record<string, unknown>;
}
if (!(plan.serverKey in cursor)) return null;
delete cursor[plan.serverKey];
return `${JSON.stringify(root, null, 2)}\n`;
}
function parseJsonObject(text: string | null, where: string): Record<string, unknown> {
if (text == null || text.trim() === '') return {};
let parsed: unknown;
try {
parsed = JSON.parse(text);
} catch (err) {
throw new Error(
`existing config at ${where} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
);
}
if (parsed == null || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error(`existing config at ${where} is not a JSON object`);
}
return parsed as Record<string, unknown>;
}
// --- Snippets for the manual (print-only) strategy ----------------------
function genericMcpServersSnippet(spec: McpLaunchSpec, name: string): string {
const server: Record<string, unknown> = {
command: spec.command,
args: spec.args,
};
if (Object.keys(spec.env).length > 0) server.env = spec.env;
return JSON.stringify({ mcpServers: { [name]: server } }, null, 2);View on GitHub (pinned to 5be4028344)
Solutions
- Open the file at the path in the error message and fix the JSON syntax error (remove trailing commas, quote keys, remove comments).
- Validate with `node -e 'JSON.parse(require("fs").readFileSync("<path>","utf8"))'` or `jq . <path>`.
- If the file is JSONC/JSON5, convert it to plain JSON before running the install.
- Back up and regenerate the file from scratch if it is beyond quick repair.
Example fix
// before (~/.config/claude/claude_desktop_config.json)
{
"mcpServers": {
"foo": { "command": "foo" }, // trailing comma below
}
}
// after
{
"mcpServers": {
"foo": { "command": "foo" }
}
} Defensive patterns
Strategy: validation
Validate before calling
import { readFile } from 'node:fs/promises';
async function assertConfigParses(configPath: string): Promise<void> {
const text = await readFile(configPath, 'utf8').catch(() => null);
if (text == null || text.trim() === '') return;
try {
JSON.parse(text);
} catch (err) {
throw new Error(`Refusing to install: ${configPath} is not valid JSON (${(err as Error).message}). Fix or back up the file first.`);
}
}
await assertConfigParses(plan.configPath); Try / catch
try {
applyJsonInstall(existingText, plan);
} catch (error) {
if (error instanceof Error && error.message.includes('is not valid JSON')) {
// Prompt the user to fix the file at plan.configPath, then retry.
throw new Error(`MCP install aborted: fix JSON syntax in ${plan.configPath} first.`);
}
throw error;
} Prevention
- Keep MCP client config files valid JSON (no comments, no trailing commas).
- Validate with `jq . <file>` or `node -e 'JSON.parse(...)'` after hand-editing.
- Back up config files before tools mutate them.
When it happens
Trigger: Running `od mcp install` (or any flow that calls applyJsonInstall/removeJsonInstall) against a target config file containing a JSON syntax error: trailing comma, unquoted key, single-quoted string, JSX/JSON5 comment (`//` or `/* */`), or a literal BOM/corruption.
Common situations: User has hand-edited their claude_desktop_config.json and left a trailing comma or comment; config was written by a tool that emits JSON5/JSONC; file was partially written and truncated; CRLF/encoding issues.
Related errors
- existing config at ${where} is not a JSON object
- Unsupported container: {config['container']}
- invalid JSON in ${filePath}: ${message}
- ${filePath} must contain a JSON object
- proposal patch.after is not valid JSON
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/5941827d5494e5fb.
Report an issue: GitHub.