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

  1. 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
  2. Check the key's spelling and casing against FffConfig in packages/pi-fff/src/config.ts (exact camelCase match is required)
  3. 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
  4. 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

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


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)