paperclipai/paperclip · error · Error
Failed to parse JSON at ${filePath}: ${err instanceof Error
Error message
Failed to parse JSON at ${filePath}: ${err instanceof Error ? err.message : String(err)} What it means
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.
Source
Thrown at cli/src/config/store.ts:44
const nextDir = path.resolve(currentDir, "..");
if (nextDir === currentDir) break;
currentDir = nextDir;
}
return null;
}
export function resolveConfigPath(overridePath?: string): string {
if (overridePath) return path.resolve(overridePath);
if (process.env.PAPERCLIP_CONFIG) return path.resolve(process.env.PAPERCLIP_CONFIG);
return findConfigFileFromAncestors(process.cwd()) ?? resolveDefaultConfigPath(resolvePaperclipInstanceId());
}
function parseJson(filePath: string): unknown {
try {
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
} catch (err) {
throw new Error(`Failed to parse JSON at ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
}
}
function migrateLegacyConfig(raw: unknown): unknown {
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return raw;
const config = { ...(raw as Record<string, unknown>) };
const databaseRaw = config.database;
if (typeof databaseRaw !== "object" || databaseRaw === null || Array.isArray(databaseRaw)) {
return config;
}
const database = { ...(databaseRaw as Record<string, unknown>) };
if (database.mode === "pglite") {
database.mode = "embedded-postgres";
if (typeof database.embeddedPostgresDataDir !== "string" && typeof database.pgliteDataDir === "string") {
database.embeddedPostgresDataDir = database.pgliteDataDir;
}View on GitHub (pinned to 67001ec6eb)
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.
Example fix
// before: .paperclip/config.json contains
// { database: { mode: "embedded-postgres", } } (unquoted key + trailing comma)
// after
{
"database": { "mode": "embedded-postgres" }
} Defensive patterns
Strategy: validation
Validate before calling
import fs from "node:fs";
function validateConfigJson(filePath: string): void {
let text: string;
try {
text = fs.readFileSync(filePath, "utf-8");
} catch (err) {
throw new Error(`Cannot read config at ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
}
try {
JSON.parse(text);
} catch (err) {
throw new Error(`Failed to parse JSON at ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
}
}
// before invoking any CLI command that reads config:
validateConfigJson(resolveConfigPath()); Type guard
function isParsableJson(text: string): boolean {
try { JSON.parse(text); return true; } catch { return false; }
} Try / catch
try {
const config = readConfig(configPath);
} catch (err) {
if (err instanceof Error && err.message.startsWith("Failed to parse JSON at ")) {
// move the corrupt file aside and regenerate
fs.copyFileSync(configPath, `${configPath}.corrupt-${Date.now()}`);
fs.rmSync(configPath, { force: true });
// fall through to a regenerating path (e.g. worktree init / default setup)
}
throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
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
- Failed to parse JSON at ${filePath}: ${err instanceof Error
- --payload must be a JSON object
- ${name} must be a JSON object
- Invalid ${name} JSON: ${err instanceof Error ? err.message :
- Company ID is required. Pass --company-id, set PAPERCLIP_COM
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/c5f2df070f6f6ee7.
Report an issue: GitHub.