{"record":{"id":"f88b7e7cb18afa24","repo":"actualbudget/actual","slug":"rate-limit-exceeded","errorCode":"RATE_LIMIT_EXCEEDED","errorMessage":"RATE_LIMIT_EXCEEDED","messagePattern":"RATE_LIMIT_EXCEEDED","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/desktop-client/src/accounts/syncStatus.ts","lineNumber":30,"sourceCode":"  );\n}\n\nexport function getFailedSyncError(\n  account: Pick<AccountEntity, 'bank_sync_status' | 'account_sync_source'>,\n): { type: string; code: string } {\n  switch (account.bank_sync_status) {\n    case 'reauth-required':\n      if (account.account_sync_source === 'simpleFin') {\n        return { type: 'INVALID_ACCESS_TOKEN', code: 'INVALID_ACCESS_TOKEN' };\n      }\n      return { type: 'ITEM_ERROR', code: 'ITEM_LOGIN_REQUIRED' };\n    case 'attention-required':\n      return {\n        type: 'ACCOUNT_NEEDS_ATTENTION',\n        code: 'ACCOUNT_NEEDS_ATTENTION',\n      };\n    case 'rate-limit-exceeded':\n      return { type: 'RATE_LIMIT_EXCEEDED', code: 'RATE_LIMIT_EXCEEDED' };\n    case 'timed-out':\n      return { type: 'TIMED_OUT', code: 'TIMED_OUT' };\n    case 'account-missing':\n      return { type: 'ACCOUNT_MISSING', code: 'ACCOUNT_MISSING' };\n    default:\n      return { type: 'SYNC_ERROR', code: 'SYNC_ERROR' };\n  }\n}\n","sourceCodeStart":12,"sourceCodeEnd":39,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/accounts/syncStatus.ts#L12-L39","documentation":"Returned by getFailedSyncError for accounts with bank_sync_status 'rate-limit-exceeded'. The aggregator rejected the last sync because the institution or provider API request quota was hit. It is temporary — the descriptor signals the user to wait rather than retry immediately.","triggerScenarios":"Calling getFailedSyncError on an account whose bank_sync_status === 'rate-limit-exceeded' after a sync attempt was throttled by the provider/bank.","commonSituations":"Very frequent manual sync clicks; scheduled syncs above the institution's allowed rate; shared provider quotas exhausted during peak hours; multiple devices triggering sync simultaneously.","solutions":["Wait for the provider's rate-limit window to pass (typically 1-24 hours) before syncing again","Reduce sync frequency / disable aggressive auto-sync schedules for the account","Trigger a single manual sync instead of repeated retries","If persistent, contact the provider (GoCardless/Plaid) about the institution's quota"],"exampleFix":"// before\nawait syncAccount(account.id); // keeps failing while rate-limited\n// after\nif (getFailedSyncError(account).code === 'RATE_LIMIT_EXCEEDED') {\n  scheduleRetry(hoursLater(24));\n}","handlingStrategy":"retry","validationCode":"const err = getFailedSyncError(account);\nif (err.code === 'RATE_LIMIT_EXCEEDED') {\n  // defer the sync call\n}","typeGuard":"function isRateLimited(v) {\n  return !!v && typeof v === 'object' && v.code === 'RATE_LIMIT_EXCEEDED';\n}","tryCatchPattern":null,"preventionTips":["Throttle manual sync button usage per account","Schedule syncs well below provider rate limits (e.g. at most every few hours)","Back off exponentially instead of retrying immediately after a limit hit"],"tags":["bank-sync","rate-limit","retry"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}