{"record":{"id":"b9adf8ff5690adb5","repo":"n8n-io/n8n","slug":"failed-to-parse-label-at-path-msg-b9adf8","errorCode":null,"errorMessage":"Failed to parse ${label} at ${path}: ${msg}","messagePattern":"Failed to parse (.+?) at (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/instance-ai/evaluations/cli/mcp-builder.ts","lineNumber":46,"sourceCode":"// MCP config staging — the `--mcp-config` file `claude -p` is pointed at\n// ---------------------------------------------------------------------------\n\nconst claudeConfigSchema = z\n\t.object({\n\t\tmcpServers: z.record(z.unknown()).optional(),\n\t\tprojects: z\n\t\t\t.record(z.object({ mcpServers: z.record(z.unknown()).optional() }).passthrough())\n\t\t\t.optional(),\n\t})\n\t.passthrough();\n\nfunction readJson(path: string, label: string): unknown {\n\tconst content = readFileSync(path, 'utf-8');\n\ttry {\n\t\treturn JSON.parse(content);\n\t} catch (error) {\n\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\tthrow new Error(`Failed to parse ${label} at ${path}: ${msg}`);\n\t}\n}\n\nfunction uniqueDefined(values: Array<string | undefined>): string[] {\n\tconst unique: string[] = [];\n\tfor (const value of values) {\n\t\tif (!value || unique.includes(value)) continue;\n\t\tunique.push(value);\n\t}\n\treturn unique;\n}\n\n/** Deduplicate project scopes (repo root, build cwd, process cwd) for the lookup. */\nexport function uniqueProjectScopes(scopes: Array<string | undefined>): string[] {\n\treturn uniqueDefined(scopes);\n}\n\n// Every staged config embeds a bearer token, so exit cleanup is intrinsic to","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/instance-ai/evaluations/cli/mcp-builder.ts#L28-L64","documentation":"A generic JSON-parse helper (readJson) used by the MCP builder. It reads a file, attempts JSON.parse, and on failure wraps the underlying SyntaxError with the file label and path so the operator knows which file is malformed. Common callers are the ~/.claude.json loader and staged MCP config files.","triggerScenarios":"Hand-editing ~/.claude.json or a staged MCP config and leaving a trailing comma, unquoted key, or unterminated object; a partial write left on disk after a crash.","commonSituations":"Manual edits to Claude Code config; merge-conflict resolution artifacts left in JSON; tooling that writes JSON without pretty-printing producing invalid output.","solutions":["Open the path named in the message and run it through a JSON linter to find the syntax error.","Restore the file from a known-good backup or regenerate it via the tool that owns it (e.g. claude code for ~/.claude.json).","Validate JSON in your editor with a JSON language server before saving."],"exampleFix":"// before: { \"mcpServers\": { \"n8n\": { ... } } , }\n// after:  { \"mcpServers\": { \"n8n\": { ... } } }","handlingStrategy":"validation","validationCode":"function readJsonChecked(path: string, label: string): unknown {\n  const text = readFileSync(path, 'utf-8');\n  try { return JSON.parse(text); }\n  catch { throw new Error(`${label} at ${path} is invalid JSON`); }\n}\n// or pre-validate: JSON.parse(readFileSync(path,'utf-8')) in a try at call site","typeGuard":"const isRecord = (v: unknown): v is Record<string, unknown> =>\n  typeof v === 'object' && v !== null && !Array.isArray(v);","tryCatchPattern":"try { return readJson(path, label); } catch (e) {\n  if (e instanceof Error && e.message.startsWith('Failed to parse')) { /* restore backup */ }\n  else throw e;\n}","preventionTips":["Never hand-edit JSON; use a tool that writes valid JSON.","Add a JSON schema check in CI for config files.","Keep backups of config files for quick restore."],"tags":["config","json","parsing","mcp"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}