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
- Open the path from the error and fix the JSON syntax — run it through JSON.parse or a linter to find the exact spot.
- Remove JSONC-only constructs (comments, trailing commas) or switch the file to strict JSON.
- Restore the file from backup or delete it so the library regenerates defaults.
- 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
- Lint the config file as strict JSON (no comments, no trailing commas) in CI.
- Strip BOM before parsing if files may come from Windows editors.
- Write config atomically (temp file + rename) to avoid truncated files.
- Keep a known-good backup of the config.
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)