{"record":{"id":"c5f2df070f6f6ee7","repo":"paperclipai/paperclip","slug":"failed-to-parse-json-at-filepath-err-instanc-c5f2df","errorCode":null,"errorMessage":"Failed to parse JSON at ${filePath}: ${err instanceof Error ? err.message : String(err)}","messagePattern":"Failed to parse JSON at (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/src/config/store.ts","lineNumber":44,"sourceCode":"    const nextDir = path.resolve(currentDir, \"..\");\n    if (nextDir === currentDir) break;\n    currentDir = nextDir;\n  }\n\n  return null;\n}\n\nexport function resolveConfigPath(overridePath?: string): string {\n  if (overridePath) return path.resolve(overridePath);\n  if (process.env.PAPERCLIP_CONFIG) return path.resolve(process.env.PAPERCLIP_CONFIG);\n  return findConfigFileFromAncestors(process.cwd()) ?? resolveDefaultConfigPath(resolvePaperclipInstanceId());\n}\n\nfunction parseJson(filePath: string): unknown {\n  try {\n    return JSON.parse(fs.readFileSync(filePath, \"utf-8\"));\n  } catch (err) {\n    throw new Error(`Failed to parse JSON at ${filePath}: ${err instanceof Error ? err.message : String(err)}`);\n  }\n}\n\nfunction migrateLegacyConfig(raw: unknown): unknown {\n  if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) return raw;\n  const config = { ...(raw as Record<string, unknown>) };\n  const databaseRaw = config.database;\n  if (typeof databaseRaw !== \"object\" || databaseRaw === null || Array.isArray(databaseRaw)) {\n    return config;\n  }\n\n  const database = { ...(databaseRaw as Record<string, unknown>) };\n  if (database.mode === \"pglite\") {\n    database.mode = \"embedded-postgres\";\n\n    if (typeof database.embeddedPostgresDataDir !== \"string\" && typeof database.pgliteDataDir === \"string\") {\n      database.embeddedPostgresDataDir = database.pgliteDataDir;\n    }","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/cli/src/config/store.ts#L26-L62","documentation":"Thrown by parseJson in the config store when JSON.parse fails on the contents of a Paperclip config file. The wrapper includes the file path and the underlying parse error message (SyntaxError text like 'Unexpected token'). parseJson is used by readConfig (which loads .paperclip/config.json) and by writeConfig (which reads the existing file to merge), so a corrupt config file surfaces this error during any config read or merge attempt.","triggerScenarios":"Any operation that triggers readConfig or writeConfig when .paperclip/config.json (or PAPERCLIP_CONFIG / ancestor config) contains invalid JSON: trailing commas, unquoted keys, single quotes, truncated file, or accidental prose. Also triggered if a hand-edit or a botched sed leaves the file malformed.","commonSituations":"A developer manually edits config.json and introduces a syntax error. Or a previous write was interrupted (power loss, kill -9) leaving a partial file despite the atomic-write protection. Or a merge tool conflict markers ('<<<<<<<') left in the JSON.","solutions":["Open the file at the reported path and fix the JSON syntax error (validate with a JSON linter or `node -e 'JSON.parse(require(\"fs\").readFileSync(\"<path>\",\"utf8\"))'`).","Restore from the automatic backup at <configPath>.backup if present.","If the file is unrecoverable, delete it and re-run `paperclipai worktree init` (or the primary setup) to regenerate a default config.","Use the backupInvalidConfig helper to move the bad file aside before rewriting."],"exampleFix":"// before: .paperclip/config.json contains\n// { database: { mode: \"embedded-postgres\", } }  (unquoted key + trailing comma)\n// after\n{\n  \"database\": { \"mode\": \"embedded-postgres\" }\n}","handlingStrategy":"validation","validationCode":"import fs from \"node:fs\";\n\nfunction validateConfigJson(filePath: string): void {\n  let text: string;\n  try {\n    text = fs.readFileSync(filePath, \"utf-8\");\n  } catch (err) {\n    throw new Error(`Cannot read config at ${filePath}: ${err instanceof Error ? err.message : String(err)}`);\n  }\n  try {\n    JSON.parse(text);\n  } catch (err) {\n    throw new Error(`Failed to parse JSON at ${filePath}: ${err instanceof Error ? err.message : String(err)}`);\n  }\n}\n\n// before invoking any CLI command that reads config:\nvalidateConfigJson(resolveConfigPath());","typeGuard":"function isParsableJson(text: string): boolean {\n  try { JSON.parse(text); return true; } catch { return false; }\n}","tryCatchPattern":"try {\n  const config = readConfig(configPath);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Failed to parse JSON at \")) {\n    // move the corrupt file aside and regenerate\n    fs.copyFileSync(configPath, `${configPath}.corrupt-${Date.now()}`);\n    fs.rmSync(configPath, { force: true });\n    // fall through to a regenerating path (e.g. worktree init / default setup)\n  }\n  throw err;\n}","preventionTips":["Never hand-edit config.json without re-validating with a JSON linter or `node -e 'JSON.parse(...)'`.","Rely on the CLI's writeConfig (atomic write + .backup) rather than external edits.","Keep the .paperclip/config.json.backup safe; restore from it when parse fails.","Watch for merge-conflict markers ('<<<<<<<') after git merges touching config.json."],"tags":["config","json","file-io","validation"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}