dmtrKovalenko/fff · error · Error

not valid JSON ( )

Error message

not valid JSON (${errorMessage(error)})

What it means

loadConfig read the fff config file successfully but JSON.parse threw, so the file contents are not valid JSON. The original parse error is embedded in the message ("not valid JSON (...)") to help locate the syntax problem. The library treats any malformed config as fatal rather than silently ignoring user settings.

Solutions

  1. Open the path from the error and fix the JSON syntax — run it through JSON.parse or a linter to find the exact spot.
  2. Remove JSONC-only constructs (comments, trailing commas) or switch the file to strict JSON.
  3. Restore the file from backup or delete it so the library regenerates defaults.
  4. Validate the config with a JSON schema/JSON.parse in CI to catch breakage early.

Example fix

// before (config.json)
{
  "keymaps": { "open": "<leader>ff", }, // trailing comma + comment
}
// after
{
  "keymaps": { "open": "<leader>ff" }
}
Defensive patterns

Strategy: try-catch

Validate before calling

const contents = fs.readFileSync(configPath, 'utf8').replace(/^\uFEFF/, '');
try { JSON.parse(contents); } catch (e) { throw new Error(`${configPath} is not valid JSON: ${e.message}`); }

Try / catch

try {
  cfg = config(configPath);
} catch (e) {
  if (/not valid JSON/.test(e.message)) {
    console.error(`Fix syntax in ${configPath}; falling back to defaults`);
    cfg = defaultConfig;
  } else throw e;
}

Prevention

When it happens

Trigger: Calling config()/loadConfig when the config file at configPath contains a JSON syntax error: trailing commas, comments (JSON5-style //), single quotes, unquoted keys, truncated writes, or BOM/control characters.

Common situations: Hand-editing the config and leaving a trailing comma; tools that wrote a partial file during a crash; copying a JSONC config with comments into a strict-JSON loader; a previous version wrote config in a different format.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of dmtrKovalenko/fff@7f8537e70f (2026-09-10). Data as JSON: /api/errors/bd9912595c27e34e. Report an issue: GitHub.

Appendix: source

Thrown at packages/pi-fff/src/config.ts:49

export function loadConfig(agentDir = piDataDir()): FffConfig {
  const configPath = join(agentDir, CONFIG_FILE_NAME);
  let contents: string;

  try {
    contents = readFileSync(configPath, "utf8");
  } catch (error: unknown) {
    if ((error as NodeJS.ErrnoException).code === "ENOENT") return {};
    throw new Error(
      `Could not read pi-fff config at ${configPath}: ${errorMessage(error)}`,
    );
  }

  let parsed: unknown;
  try {
    parsed = JSON.parse(contents);
  } catch (error: unknown) {
    throw invalidConfig(configPath, `not valid JSON (${errorMessage(error)})`);
  }

  if (!isRecord(parsed)) {
    throw invalidConfig(configPath, "expected a JSON object");
  }

  for (const key of Object.keys(parsed)) {
    if (!CONFIG_KEYS.has(key as keyof FffConfig)) {
      throw invalidConfig(configPath, `unknown option "${key}"`);
    }
  }

  if (parsed.mode !== undefined && !VALID_MODES.includes(parsed.mode as FffMode)) {
    throw invalidConfig(configPath, `"mode" must be one of ${VALID_MODES.join(", ")}`);
  }

  validateString(configPath, parsed, "$schema");
  validateString(configPath, parsed, "frecencyDbPath");

View on GitHub (pinned to 7f8537e70f)