actualbudget/actual · error · Error

Invalid config file: key "${key}" must be a non-negative int

Error message

Invalid config file: key "${key}" must be a non-negative integer

What it means

validateConfigFileContent type-checks every key in the parsed config file against known key schemas. For keys listed in numberKeys (numeric settings), the value must be a number that is an integer and >= 0. This error means a config file key that expects a non-negative integer was given a non-number, a float, or a negative value.

Source

Thrown at packages/cli/src/config.ts:94

  for (const key of Object.keys(value)) {
    if (!configFileKeys.includes(key)) {
      throw new Error(`Invalid config file: unknown key "${key}"`);
    }
    const v = value[key];
    if (v === undefined) continue;
    if (
      (stringKeys as readonly string[]).includes(key) &&
      typeof v !== 'string'
    ) {
      throw new Error(
        `Invalid config file: key "${key}" must be a string, got ${typeof v}`,
      );
    }
    if (
      (numberKeys as readonly string[]).includes(key) &&
      (typeof v !== 'number' || !Number.isInteger(v) || v < 0)
    ) {
      throw new Error(
        `Invalid config file: key "${key}" must be a non-negative integer`,
      );
    }
    if (
      (booleanKeys as readonly string[]).includes(key) &&
      typeof v !== 'boolean'
    ) {
      throw new Error(
        `Invalid config file: key "${key}" must be a boolean, got ${typeof v}`,
      );
    }
  }
  return value as ConfigFileContent;
}

async function loadConfigFile(): Promise<ConfigFileContent> {
  const explorer = cosmiconfig('actual', {
    searchStrategy: 'global',

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Open the config file and convert the offending key's value to a plain non-negative integer literal (remove quotes, no decimals, no negative sign).
  2. If the key was meant to be disabled, remove the key entirely so the default is used instead.
  3. Validate the JSON with a quick check: the value must satisfy Number.isInteger(v) && v >= 0.

Example fix

// before (config.json)
{ "cacheTtl": "3600" }
// after
{ "cacheTtl": 3600 }
Defensive patterns

Strategy: validation

Validate before calling

function isValidConfigNumbers(cfg: Record<string, unknown>, numberKeys: readonly string[]): boolean {
  return numberKeys.every(k => {
    const v = cfg[k];
    return v === undefined || (typeof v === 'number' && Number.isInteger(v) && v >= 0);
  });
}

Type guard

function isNonNegativeInt(v: unknown): v is number {
  return typeof v === 'number' && Number.isInteger(v) && v >= 0;
}

Try / catch

let fileConfig: ConfigFileContent;
try {
  fileConfig = loadConfigFile(path);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Invalid config file')) {
    console.error(`Config file ${path} has a bad value: ${err.message}`);
    process.exit(1);
  }
  throw err;
}

Prevention

When it happens

Trigger: A config file (loaded by loadConfigFile) contains a key in numberKeys whose value is a string (e.g. "cacheTtl": "60"), a negative number, or a non-integer float like 1.5.

Common situations: Hand-editing the config JSON and quoting numbers; setting a TTL/timeout to -1 intending 'unlimited'; pasting values from docs that use strings for durations.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/a77fd7f2ce27e867. Report an issue: GitHub.