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

  1. 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"))'`).
  2. Restore from the automatic backup at <configPath>.backup if present.
  3. If the file is unrecoverable, delete it and re-run `paperclipai worktree init` (or the primary setup) to regenerate a default config.
  4. 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

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

Related errors


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/c5f2df070f6f6ee7. Report an issue: GitHub.