actualbudget/actual · error

ITEM_LOGIN_REQUIRED

ITEM_LOGIN_REQUIRED

Error message

ITEM_ERROR

What it means

Returned by getFailedSyncError for accounts whose bank_sync_status is 'reauth-required' with a non-simpleFin source (e.g. goCardless). It mirrors the Plaid-style ITEM_ERROR / ITEM_LOGIN_REQUIRED contract: the bank item's login credentials or consent have lapsed and the user must log in again through the provider's link flow.

Source

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

) {
  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. Trigger the Re-link flow for the account and re-authenticate with the bank
  2. Check the provider requisition status and recreate it if expired
  3. After successful re-link, sync the account to clear 'reauth-required'
  4. Set a reminder to re-auth before consent expiry if the bank enforces SCA cycles

Example fix

// before
const err = getFailedSyncError(account); // ITEM_LOGIN_REQUIRED
// after
await openBankLinkFlow(account.id); // user re-enters bank credentials
Defensive patterns

Strategy: validation

Validate before calling

function needsBankRelink(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 with bank_sync_status === 'reauth-required' and account_sync_source other than 'simpleFin'.

Common situations: Bank password changed; user changed online banking credentials; bank requires periodic re-consent (PSD2/SCA with GoCardless); provider marks the item invalid.

Related errors


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