{"record":{"id":"7e43a64166b00f45","repo":"actualbudget/actual","slug":"unrecognized-sync-source-string-syncsource","errorCode":null,"errorMessage":"Unrecognized sync source: ${String(syncSource)}","messagePattern":"Unrecognized sync source: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/components/modals/SelectLinkedAccountsModal.tsx","lineNumber":243,"sourceCode":"            syncSource: 'akahu',\n            externalAccounts: toSort as SyncServerAkahuAccount[],\n            upgradingAccountId,\n          };\n        case 'goCardless':\n          return {\n            syncSource: 'goCardless',\n            requisitionId: requisitionId!,\n            externalAccounts: toSort as SyncServerGoCardlessAccount[],\n            upgradingAccountId,\n          };\n        case 'enableBanking':\n          return {\n            syncSource: 'enableBanking',\n            externalAccounts: toSort as SyncServerEnableBankingAccount[],\n            upgradingAccountId,\n          };\n        default:\n          throw new Error(`Unrecognized sync source: ${String(syncSource)}`);\n      }\n    }, [externalAccounts, syncSource, requisitionId, upgradingAccountId]);\n\n  const { t } = useTranslation();\n  const { isNarrowWidth } = useResponsive();\n  const dispatch = useDispatch();\n  const { data: allAccounts = [] } = useAccounts();\n  const localAccounts = allAccounts.filter(a => a.closed === 0);\n  const { initialDraftLinkAccounts, initiallyChosenAccounts } = useMemo(\n    () =>\n      computeInitialLinkState(\n        localAccounts,\n        propsWithSortedExternalAccounts.externalAccounts,\n        upgradingAccountId,\n      ),\n    [\n      localAccounts,\n      propsWithSortedExternalAccounts.externalAccounts,","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/components/modals/SelectLinkedAccountsModal.tsx#L225-L261","documentation":"SelectLinkedAccountsModal normalizes its props in a useMemo switch over `syncSource`, which supports 'simpleFin', 'pluggyai', 'akahu', 'goCardless', and 'enableBanking'. Any other value falls to `default` and throws. This guards against the modal being opened with an unsupported bank-sync provider string.","triggerScenarios":"Opening SelectLinkedAccountsModal with a `syncSource` prop that isn't one of the five supported providers — e.g. a typo ('simplefin' vs 'simpleFin'), a newly added provider not yet handled here, or undefined/null passed by a caller.","commonSituations":"Adding a new bank-sync backend (or regional provider) and wiring the modal before extending the switch; case-sensitive string mismatches in navigation code; callers passing the provider name in a different format than the union expects.","solutions":["Pass one of the supported values: 'simpleFin' | 'pluggyai' | 'akahu' | 'goCardless' | 'enableBanking'.","Fix casing/typos — the check is exact ('simpleFin' with capital F, 'goCardless' with capital C).","If a new provider was added to the backend, add its case to the useMemo switch and extend the props union.","Validate syncSource at the call site before dispatching the modal."],"exampleFix":"// before\n<SelectLinkedAccountsModal\n  syncSource={source.toLowerCase()}  // 'goCardless'.toLowerCase() => 'gocardless' => throws\n// after\nconst SYNC_SOURCES = ['simpleFin', 'pluggyai', 'akahu', 'goCardless', 'enableBanking'] as const;\ntype SyncSource = typeof SYNC_SOURCES[number];\nif (!SYNC_SOURCES.includes(source as SyncSource)) {\n  throw new Error(`Unsupported sync source: ${source}`); // fail at call site\n}","handlingStrategy":"validation","validationCode":"const SYNC_SOURCES = ['simpleFin', 'pluggyai', 'akahu', 'goCardless', 'enableBanking'] as const;\ntype SyncSource = typeof SYNC_SOURCES[number];\nif (!syncSource || !SYNC_SOURCES.includes(syncSource as SyncSource)) {\n  console.error('Cannot open linked-accounts modal: bad syncSource', syncSource);\n  return; // don't dispatch the modal\n}","typeGuard":"type SyncSource = 'simpleFin' | 'pluggyai' | 'akahu' | 'goCardless' | 'enableBanking';\nfunction isSyncSource(v: unknown): v is SyncSource {\n  return typeof v === 'string' &&\n    ['simpleFin', 'pluggyai', 'akahu', 'goCardless', 'enableBanking'].includes(v);\n}","tryCatchPattern":"try {\n  dispatch(pushModal({ name: 'select-linked-accounts', syncSource }));\n} catch (err) {\n  if (String(err).includes('Unrecognized sync source')) {\n    dispatch(addNotification({ message: t('Unsupported bank provider') }));\n  } else {\n    throw err;\n  }\n}","preventionTips":["Import and reuse the exported SyncSource union type at every call site instead of raw strings.","Match casing exactly: 'simpleFin' and 'goCardless' are mixed-case.","When adding a new bank-sync backend, update the union, the switch, and the type guard together."],"tags":["react","bank-sync","validation","switch-statement"],"backgroundTag":"unsupported-provider","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}