actualbudget/actual · error · FileNotFound

File does not exist or you don't have access to it

Error message

File does not exist or you don't have access to it

What it means

FileNotFound is thrown by FilesService.get() when no row exists for the given fileId, or the row exists but is soft-deleted (deleted flag set). It is the canonical 'this file is not accessible to you' error of the app-sync files service.

Source

Thrown at packages/sync-server/src/app-sync/services/files-service.ts:142

  encrypt_meta: string | null;
  encrypt_salt: string | null;
  encrypt_test: string | null;
  encrypt_keyid: string | null;
  deleted: number;
  owner: string | null;
};

class FilesService {
  accountDb: WrappedDatabase;

  constructor(accountDb: WrappedDatabase) {
    this.accountDb = accountDb;
  }

  get(fileId: FileId) {
    const rawFile = this.getRaw(fileId);
    if (!rawFile || (rawFile && rawFile.deleted)) {
      throw new FileNotFound();
    }

    return this.validate(rawFile);
  }

  set(file: File) {
    const deletedInt = boolToInt(file.deleted);
    this.accountDb.mutate(
      'INSERT INTO files (id, group_id, sync_version, name, encrypt_meta, encrypt_salt, encrypt_test, encrypt_keyid, deleted, owner) VALUES (?, ?, ?, ?, ?, ?, ?, ? ,?, ?)',
      [
        file.id,
        file.groupId,
        file.syncVersion?.toString(),
        file.name,
        file.encryptMeta,
        file.encryptSalt,
        file.encryptTest,
        file.encryptKeyId,

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Verify the file ID / sync ID used by the client exists in the files table of the account database.
  2. Confirm the client is talking to the correct sync server instance and uses the same user credentials.
  3. If the file was deleted, create/upload the file again or restore it from a backup.
  4. Clear stale local budget metadata and re-download or reset sync for that file.

Example fix

// before
await client.getFile('dead-file-id');   // FileNotFound
// after
const files = await client.listFiles();
const file = files.find(f => f.groupId === myGroupId);
await client.getFile(file.fileId);
Defensive patterns

Strategy: try-catch

Validate before calling

// before fetching, confirm the id exists via list/find
const known = await client.listKnownFileIds?.() ?? [];
if (!known.includes(fileId)) console.warn(`File ${fileId} not in server list`);

Type guard

function isFileNotFound(err: unknown): err is { type: 'file-not-found' } {
  return typeof err === 'object' && err !== null && (err as any).type === 'file-not-found';
}

Try / catch

try {
  const file = filesService.get(fileId);
  return file;
} catch (err) {
  if (isFileNotFound(err)) {
    return null; // treat as deleted / never existed; prompt user to re-create or reset sync
  }
  throw err;
}

Prevention

When it happens

Trigger: Any read path (verifyFileExists, sync handlers) calling get(fileId) with: an unknown ID, an ID belonging to another user after ownership validation, or a file that has been deleted (DELETE endpoint) but whose ID is still cached client-side.

Common situations: Client points at a different server/directory than where the budget was created; the budget file was deleted from another device; stale local metadata referencing an old group; database was reset or migrated.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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