{"record":{"id":"abe0c4fd72f6b870","repo":"actualbudget/actual","slug":"no-data","errorCode":"NO_DATA","errorMessage":"NO_DATA","messagePattern":"NO_DATA","errorType":"error_code","errorClass":"BankSyncError","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/accounts/sync.ts","lineNumber":220,"sourceCode":"    res = await post(\n      getServer().SIMPLEFIN_SERVER + '/transactions',\n      {\n        accountId: acctId,\n        startDate: since,\n      },\n      {\n        'X-ACTUAL-TOKEN': userToken,\n      },\n      // 5 minute timeout for batch sync, one minute for individual accounts\n      Array.isArray(acctId) ? 300000 : 60000,\n    );\n  } catch (error) {\n    logger.error('Suspected timeout during bank sync:', error);\n    throw BankSyncError('TIMED_OUT', 'TIMED_OUT');\n  }\n\n  if (Object.keys(res).length === 0) {\n    throw BankSyncError('NO_DATA', 'NO_DATA');\n  }\n  if (res.error_code) {\n    throw BankSyncError(res.error_type, res.error_code);\n  }\n\n  let retVal = {};\n  if (batchSync) {\n    const batchErrors = res.errors;\n    for (const accountId of Object.keys(res)) {\n      if (accountId === 'errors') continue;\n\n      const data = res[accountId];\n      const error = batchErrors?.[accountId]?.[0];\n\n      retVal[accountId] = {\n        transactions: data?.transactions?.all,\n        accountBalance: data?.balances,\n        startingBalance: data?.startingBalance,","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/accounts/sync.ts#L202-L238","documentation":"After a successful SimpleFin response, the code checks Object.keys(res).length === 0 and throws BankSyncError('NO_DATA', 'NO_DATA') (sync.ts:219-221). This means SimpleFin returned an empty object — no account data at all — even though the request itself completed. It typically indicates the account ids or date range matched nothing on the SimpleFin side.","triggerScenarios":"The SimpleFin /transactions response body is an empty object, e.g. the requested acctId(s) are no longer in the SimpleFin account set or the startDate filter excludes everything.","commonSituations":"SimpleFin account was removed or renamed on the provider side; syncing with a malformed/stale account id; SimpleFin returned a 200 with no data after a bank re-auth was required; first-time sync before the bank finished aggregating.","solutions":["Re-fetch the SimpleFin accounts list (GET on the SimpleFin access URL) and confirm the acctId still exists, then resync with the current id.","Re-run the SimpleFin claims/setup flow to refresh the access URL if the account set changed.","Remove any startDate restriction or widen the date range so data falls inside the window.","Check the account on SimpleFin's dashboard for pending re-authentication of the underlying bank."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Confirm the account exists in SimpleFin before syncing:\nconst accounts = await fetch(simplefinAccessUrl + '/accounts', {...}).then(r => r.json());\nif (!accounts.accounts?.some(a => a.id === acctId)) {\n  throw new Error(`Account ${acctId} not present in SimpleFin — refresh the access URL`);\n}","typeGuard":"function isEmptyResponse(res: unknown): boolean {\n  return typeof res === 'object' && res !== null && Object.keys(res).length === 0;\n}","tryCatchPattern":"try {\n  await syncAccount(accountId);\n} catch (e) {\n  if ((e as { errorCode?: string }).errorCode === 'NO_DATA') {\n    // re-fetch SimpleFin accounts / prompt bank re-auth, or skip gracefully\n  } else throw e;\n}","preventionTips":["Periodically refresh the SimpleFin access/claims URL and re-list accounts.","Handle NO_DATA as 'needs re-link or no data yet' rather than a hard failure in scheduled syncs.","Skip gracefully on first syncs before the bank finishes aggregating.","Warn users when a synced account disappears from the provider's account list."],"tags":["bank-sync","simplefin","empty-response","data"],"backgroundTag":"empty-api-response","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}