{"record":{"id":"9e29c79445bb3864","repo":"actualbudget/actual","slug":"response-reason-response-error-fallbackmessa","errorCode":null,"errorMessage":"response.reason || response.error || fallbackMessage","messagePattern":"response\\.reason \\|\\| response\\.error \\|\\| fallbackMessage","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/desktop-client/src/components/banksync/useBuiltInBankSyncProviders.ts","lineNumber":103,"sourceCode":"  }\n\n  if (isAdmin) {\n    return null;\n  }\n\n  return isFileOwner ? 'file-owner' : 'general';\n}\n\nasync function ensureSuccessResponse(\n  response: SecretSetResponse,\n  fallbackMessage: string,\n) {\n  if (response?.error_code) {\n    throw new Error(response.reason || response.error_code);\n  }\n\n  if (response?.error) {\n    throw new Error(response.reason || response.error || fallbackMessage);\n  }\n}\n\nexport function useBuiltInBankSyncProviders({\n  upgradingAccountId,\n}: UseBuiltInBankSyncProvidersOptions = {}) {\n  const { t } = useTranslation();\n  const dispatch = useDispatch();\n  const syncServerStatus = useSyncServerStatus();\n  const { cloudFileId, isAdmin, isFileOwner } = useCurrentAccess();\n  const canConfigureProviders = isAdmin;\n\n  const [isGoCardlessSetupComplete, setIsGoCardlessSetupComplete] = useState<\n    boolean | null\n  >(null);\n  const [isSimpleFinSetupComplete, setIsSimpleFinSetupComplete] = useState<\n    boolean | null\n  >(null);","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/desktop-client/src/components/banksync/useBuiltInBankSyncProviders.ts#L85-L121","documentation":"The second branch of `ensureSuccessResponse`: when the sync-server response carries an `error` (without an `error_code`), it throws `response.reason || response.error || fallbackMessage`. The `fallbackMessage` (passed by each provider's reset handler, e.g. 'Failed to set GoCardless secret') is only used when neither `reason` nor `error` carry text. This is the generic failure path for provider secret-set calls.","triggerScenarios":"A reset/save of any bank-sync provider secret (GoCardless, SimpleFin, PluggyAI, EnableBanking, Akahu) where the sync-server returns `{ error: '...' }` with no `error_code` — server-side exception during secret storage, network/DB failure on the server, or provider upstream call failing with only a free-form error string.","commonSituations":"Self-hosted sync-server with database/storage issues; server unreachable mid-request returning a wrapped error; provider API outage causing upstream failure surfaced as a plain error; unauthenticated request to the sync-server.","solutions":["Check the sync-server logs at the time of the request — the thrown message's `reason`/`error` text points to the server-side cause.","Verify the sync-server is online and reachable (`syncServerStatus`) and the user has admin rights before configuring providers.","Confirm server storage (secrets table / key store) is writable and the server version matches the client.","Retry the credential submission after fixing the server-side condition; if the message equals the generic fallback only, add logging to capture the full server response."],"exampleFix":"// before\nawait ensureSuccessResponse(res, 'Failed to set Akahu secret');\n// after\ntry {\n  await ensureSuccessResponse(res, 'Failed to set Akahu secret');\n} catch (e) {\n  logger.error('Akahu secret-set failed', { response: res });\n  setError(e.message === 'Failed to set Akahu secret'\n    ? 'Unknown server error — check sync-server logs'\n    : e.message);\n}","handlingStrategy":"fallback","validationCode":"if (syncServerStatus !== 'online') {\n  setError('Bank sync requires a connected sync server. Start the server and sign in first.');\n  return;\n}\nif (!isAdmin) { setError('Only admins can configure bank-sync providers.'); return; }","typeGuard":"function hasServerError(\n  res: SecretSetResponse,\n): res is SecretSetResponse & { error: string } {\n  return typeof res?.error === 'string' && res.error.length > 0;\n}","tryCatchPattern":"try {\n  await ensureSuccessResponse(res, 'Failed to set secret');\n} catch (e) {\n  const msg = e instanceof Error ? e.message : String(e);\n  if (msg === fallbackMessage) {\n    // Neither reason nor error had text — capture raw response for support\n    logger.error('secret-set failed with no reason', { res });\n    setError(fallbackMessage + ' (check sync-server logs for details)');\n  } else {\n    setError(msg);\n  }\n}","preventionTips":["Ensure the sync-server is online and reachable before opening provider settings.","Verify the server's secrets storage (database) is healthy and writable.","Keep client and server versions aligned so error responses always include `reason` text.","Log the full server response on failure so the generic fallback message never hides the root cause."],"tags":["bank-sync","sync-server","server-error","provider-credentials"],"backgroundTag":"provider-credentials-rejected","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}