actualbudget/actual · error

INVALID_ACCESS_TOKEN

INVALID_ACCESS_TOKEN

Error message

INVALID_ACCESS_TOKEN

What it means

This is not a thrown exception but an error descriptor returned by getFailedSyncError when a bank-synced account's status is 'reauth-required' and its sync source is simpleFin. It means the SimpleFin access token/credentials are no longer valid and the bank link must be re-authenticated before syncing can resume. The UI uses the descriptor to render an error banner with a re-link action.

Source

Thrown at packages/desktop-client/src/accounts/syncStatus.ts:21

export function isAccountFailedSync(
  account: Pick<AccountEntity, 'bank_sync_status'>,
) {
  const status = account.bank_sync_status;
  return (
    status != null &&
    status !== 'ok' &&
    status !== 'pending' &&
    status !== 'sync-requested'
  );
}

export function getFailedSyncError(
  account: Pick<AccountEntity, 'bank_sync_status' | 'account_sync_source'>,
): { type: string; code: string } {
  switch (account.bank_sync_status) {
    case 'reauth-required':
      if (account.account_sync_source === 'simpleFin') {
        return { type: 'INVALID_ACCESS_TOKEN', code: 'INVALID_ACCESS_TOKEN' };
      }
      return { type: 'ITEM_ERROR', code: 'ITEM_LOGIN_REQUIRED' };
    case 'attention-required':
      return {
        type: 'ACCOUNT_NEEDS_ATTENTION',
        code: 'ACCOUNT_NEEDS_ATTENTION',
      };
    case 'rate-limit-exceeded':
      return { type: 'RATE_LIMIT_EXCEEDED', code: 'RATE_LIMIT_EXCEEDED' };
    case 'timed-out':
      return { type: 'TIMED_OUT', code: 'TIMED_OUT' };
    case 'account-missing':
      return { type: 'ACCOUNT_MISSING', code: 'ACCOUNT_MISSING' };
    default:
      return { type: 'SYNC_ERROR', code: 'SYNC_ERROR' };
  }
}

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Re-authenticate the SimpleFin link via the account's Re-link flow to obtain a fresh SimpleFin access token
  2. Verify the SimpleFin setup URL/token is still valid on the SimpleFin dashboard
  3. After re-linking, run a sync to confirm bank_sync_status clears to 'ok'
  4. If re-auth keeps failing, delete and re-add the account link with a new SimpleFin setup URL

Example fix

// before
const err = getFailedSyncError(account); // INVALID_ACCESS_TOKEN surfaces
// after
await relinkAccount(account.id); // re-run SimpleFin auth, then sync
Defensive patterns

Strategy: validation

Validate before calling

function needsSimpleFinReauth(account) {
  return account.bank_sync_status === 'reauth-required' &&
    account.account_sync_source === 'simpleFin';
}

Type guard

function isSyncErrorDescriptor(v) {
  return !!v && typeof v === 'object' && typeof v.type === 'string' && typeof v.code === 'string';
}

Prevention

When it happens

Trigger: Calling getFailedSyncError on an account whose bank_sync_status === 'reauth-required' and account_sync_source === 'simpleFin' (e.g. rendering account sync status after a failed SimpleFin refresh).

Common situations: SimpleFin revoking or expiring an access token; user revoking access on the bank side; SimpleFin returning 401 during account refresh; tokens invalidated after bank credential changes.

Related errors


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