actualbudget/actual · error

File not found at the provided path: ${filepath}

Error message

File not found at the provided path: ${filepath}

What it means

`importBudget` accepts either a `filepath` or an in-memory `buffer` of a budget file to import. When a `filepath` is supplied, the function checks `fs.exists(filepath)` first and throws this error if the file cannot be found, before any parsing happens. It exists to give a clear, path-including message instead of a downstream read/parse failure.

Source

Thrown at packages/loot-core/src/server/budgetfiles/app.ts:490

  type,
}: {
  type: ImportableBudgetType;
  /** Path to the file to import, on the engine's filesystem. */
  filepath?: string;
  /** Raw contents of the file to import; alternative to `filepath`. */
  buffer?: ArrayBuffer;
  /**
   * Original name of the imported file; only used together with `buffer`,
   * to derive the budget name for some import types.
   */
  filename?: string;
}): Promise<{ error?: string; meta?: unknown; id?: string }> {
  try {
    let contents: Buffer;
    let name: string;
    if (filepath != null) {
      if (!(await fs.exists(filepath))) {
        throw new Error(`File not found at the provided path: ${filepath}`);
      }

      contents = Buffer.from(await fs.readFile(filepath, 'binary'));
      name = filepath;
    } else if (buffer != null) {
      contents = Buffer.from(buffer);
      name = filename || 'budget-import';
    } else {
      throw new Error('Either `filepath` or `buffer` must be given');
    }

    const results = await handleBudgetImport(type, name, contents);
    if (results && results.error) {
      return results;
    }
    // A successful import leaves the imported budget loaded
    return { id: prefs.getPrefs()?.id };
  } catch (err) {

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Check the file exists at the exact path using `fs.existsSync` / `fs/promises.stat` before calling `importBudget`, and confirm the path printed in the error.
  2. Expand `~` and convert relative paths to absolute with `path.resolve` — Node does not expand `~` and resolves relative to `process.cwd()`.
  3. In desktop/server contexts, remember only server-accessible paths work; pass a `buffer` instead of a `filepath` if the file lives on the client.
  4. Fix volume mounts/container paths so the file is visible to the process running Actual.

Example fix

// before
await importBudget({ type: 'ynab4', filepath: '~/exports/budget.yfull' });

// after
import path from 'path';
import os from 'os';
import fs from 'fs/promises';
const filepath = path.resolve('~/exports/budget.yfull'.replace(/^~/, os.homedir()));
await fs.access(filepath); // throws early if missing
await importBudget({ type: 'ynab4', filepath });
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'fs/promises';
import path from 'path';
import os from 'os';

async function assertImportFileExists(filepath) {
  const abs = path.resolve(String(filepath).replace(/^~/, os.homedir()));
  const stat = await fs.stat(abs);          // throws ENOENT early with the real path
  if (!stat.isFile()) throw new Error(`Not a file: ${abs}`);
  return abs;
}

Type guard

async function isReadableFile(filepath) {
  try {
    return (await fs.stat(filepath)).isFile();
  } catch {
    return false;
  }
}

Try / catch

try {
  await importBudget({ type, filepath });
} catch (e) {
  if (e.message.startsWith('File not found')) {
    console.error(`Check the path: ${e.message} (cwd=${process.cwd()})`);
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling `importBudget({ type, filepath, ... })` where the path does not exist: a typo in the filename, a relative path resolved against the wrong working directory, the file was deleted/moved before the call, or a path from another machine (e.g. a browser-side path passed to a Node-side API, where it never resolves on the server).

Common situations: Automation scripts using `~/file.yclf` without shell expansion (tilde is not expanded by Node); importing a CSV/YNAB export in a container where the file was not volume-mounted; a UI passing a fake path from a file picker's security-layer name; running the import from a different cwd than expected.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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