dmtrKovalenko/fff · error · Error
unknown option
Error message
unknown option "${key}" What it means
loadConfig validates every top-level key of pi-fff.json against the fixed set of known options ($schema, mode, frecencyDbPath, historyDbPath, enableFsRootScanning, enableHomeDirScanning, warnOnHomeDirScan, followSymlinks). If the JSON object contains any key outside that allowlist, loadConfig throws `Invalid pi-fff config at <path>: unknown option "<key>"` to fail fast on typos or stale options instead of silently ignoring them.
Solutions
- Open the config file named in the error message and remove or rename the reported key to a valid one: mode, frecencyDbPath, historyDbPath, enableFsRootScanning, enableHomeDirScanning, warnOnHomeDirScan, followSymlinks, $schema
- Check the key's spelling and casing against FffConfig in packages/pi-fff/src/config.ts (exact camelCase match is required)
- If the option was removed or renamed in a newer/older version of the package, align your config with the docs for the installed version
- If the option is genuinely useful, file an issue/PR to add it to CONFIG_KEYS rather than bypassing validation
Example fix
// before (pi-fff.json)
{
"$schema": "https://example.com/pi-fff.schema.json",
"frecencyPath": "~/.local/share/pi/frecency",
"Mode": "tools-only"
}
// after
{
"$schema": "https://example.com/pi-fff.schema.json",
"frecencyDbPath": "~/.local/share/pi/frecency",
"mode": "tools-only"
} Defensive patterns
Strategy: validation
Validate before calling
const CONFIG_KEYS = new Set(["$schema","mode","frecencyDbPath","historyDbPath","enableFsRootScanning","enableHomeDirScanning","warnOnHomeDirScan","followSymlinks"]);
const parsed = JSON.parse(readFileSync(configPath, "utf8"));
const unknown = Object.keys(parsed).filter((k) => !CONFIG_KEYS.has(k));
if (unknown.length > 0) {
throw new Error(`${configPath}: unknown option(s) ${unknown.map((k) => `"${k}"`).join(", ")}`);
} Type guard
function isFffConfig(v: unknown): v is Record<string, unknown> & FffConfig {
if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
const keys = new Set(["$schema","mode","frecencyDbPath","historyDbPath","enableFsRootScanning","enableHomeDirScanning","warnOnHomeDirScan","followSymlinks"]);
return Object.keys(v).every((k) => keys.has(k));
} Try / catch
try {
const config = loadConfig(agentDir);
} catch (err) {
if (err instanceof Error && err.message.includes("unknown option")) {
console.error("Fix or remove the unknown key in your pi-fff.json:", err.message);
const config = {}; // fall back to defaults
} else {
throw err;
}
} Prevention
- Validate pi-fff.json against the $schema or the FffConfig interface before deploying it
- Copy option names exactly from the README/docs; keys are case-sensitive camelCase
- After upgrading the package, diff your config against the current documented options
- Keep the config minimal — only set options you have verified exist
- Run a quick sanity script that JSON-parses the file and checks keys against CONFIG_KEYS before launching
When it happens
Trigger: Any top-level key in the pi-fff.json config file that is not in CONFIG_KEYS — e.g. a typo like "frecencyPath" instead of "frecencyDbPath", a stale/renamed option from an older release, or a mispasted key from another tool's config. CaSe matters: "Mode" or "mode:" variants also fail since the check is exact-string via Set.has.
Common situations: Hand-editing the config file and typo-ing a key; upgrading/downgrading the package and copying an example config that references options renamed or removed in this version; following outdated docs or a blog post that used an old option name; copy-pasting a config from a similarly named plugin.
Related errors
- expected a JSON object
- not valid JSON ( )
- Could not read pi-fff config at
- patterns array must have at least 1 element
- Path constraint must be relative to the workspace
AI-assisted analysis of dmtrKovalenko/fff@7f8537e70f (2026-09-10).
Data as JSON: /api/errors/c3cea87bd0813762.
Report an issue: GitHub.
Appendix: source
Thrown at packages/pi-fff/src/config.ts:58
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");
validateString(configPath, parsed, "historyDbPath");
validateBoolean(configPath, parsed, "enableFsRootScanning");
validateBoolean(configPath, parsed, "enableHomeDirScanning");
validateBoolean(configPath, parsed, "warnOnHomeDirScan");
validateBoolean(configPath, parsed, "followSymlinks");
return parsed as FffConfig;
}
View on GitHub (pinned to 7f8537e70f)