{"record":{"id":"fc5cb5f9e381ad0a","repo":"we-promise/sure","slug":"rate-limited-fc5cb5","errorCode":"rate_limited","errorMessage":"SimpleFin rate limit exceeded. Please try again later.","messagePattern":"SimpleFin rate limit exceeded\\. Please try again later\\.","errorType":"exception","errorClass":"Provider::Simplefin::SimplefinError","httpStatus":429,"severity":"warning","filePath":"app/models/provider/simplefin.rb","lineNumber":97,"sourceCode":"    # Use retry logic with exponential backoff for transient network failures\n    # Use self.class.get to inherit class-level SSL and timeout defaults\n    response = with_retries(\"GET /accounts\") do\n      self.class.get(accounts_url)\n    end\n\n    case response.code\n    when 200\n      JSON.parse(response.body, symbolize_names: true)\n    when 400\n      Rails.logger.error \"SimpleFin API: Bad request - #{response.body}\"\n      raise SimplefinError.new(\"Bad request to SimpleFin API: #{response.body}\", :bad_request)\n    when 403\n      raise SimplefinError.new(\"Access URL is no longer valid\", :access_forbidden)\n    when 402\n      raise SimplefinError.new(\"Payment required to access this account\", :payment_required)\n    when 429\n      Rails.logger.warn \"SimpleFin API: Rate limited - #{response.body}\"\n      raise SimplefinError.new(\"SimpleFin rate limit exceeded. Please try again later.\", :rate_limited)\n    when 500..599\n      Rails.logger.error \"SimpleFin API: Server error - Code: #{response.code}, Body: #{response.body}\"\n      raise SimplefinError.new(\"SimpleFin server error (#{response.code}). Please try again later.\", :server_error)\n    else\n      Rails.logger.error \"SimpleFin API: Unexpected response - Code: #{response.code}, Body: #{response.body}\"\n      raise SimplefinError.new(\"Failed to fetch accounts: #{response.code} #{response.message} - #{response.body}\", :fetch_failed)\n    end\n  end\n\n  def get_info(base_url)\n    # Use self.class.get to inherit class-level SSL and timeout defaults\n    response = self.class.get(\"#{base_url}/info\")\n\n    case response.code\n    when 200\n      response.body.strip.split(\"\\n\")\n    else\n      raise SimplefinError.new(\"Failed to get server info: #{response.code} #{response.message}\", :info_failed)","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/simplefin.rb#L79-L115","documentation":"Raised by Provider::Simplefin#get_accounts on HTTP 429, error_type :rate_limited. Important difference from the Redbark client: SimpleFin's with_retries only retries network exceptions (RETRYABLE_ERRORS) — the status-code case statement runs outside the retry block, so a 429 is NOT retried internally and reaches the caller on the first occurrence.","triggerScenarios":"Polling the same access URL too frequently, several accounts on one bridge syncing concurrently, or scheduled jobs bunching up against the bridge's rate limit.","commonSituations":"Autosync interval shorter than the bridge tolerates; multiple app instances (staging+prod) sharing one bridge; retry loops at the caller level amplifying request volume.","solutions":["Implement caller-side backoff on :rate_limited — the library will not retry it for you","Increase the interval between scheduled SimpleFin syncs","Serialize syncs per bridge instead of hitting several accounts in parallel","Check the response body in logs for the bridge's stated limit/window"],"exampleFix":"# before\nclient.get_accounts(access_url) # no retry on 429\n\n# after\nattempt = 0\nbegin\n  client.get_accounts(access_url)\nrescue Provider::Simplefin::SimplefinError => e\n  raise unless e.error_type == :rate_limited\n  raise if (attempt += 1) >= 3\n  sleep(30 * attempt + rand(10))\n  retry\nend","handlingStrategy":"retry","validationCode":null,"typeGuard":"def simplefin_rate_limited?(error)\n  error.is_a?(Provider::Simplefin::SimplefinError) && error.error_type == :rate_limited\nend","tryCatchPattern":"attempt = 0\nbegin\n  client.get_accounts(access_url)\nrescue Provider::Simplefin::SimplefinError => e\n  raise unless e.error_type == :rate_limited\n  raise if (attempt += 1) >= 3\n  sleep((2**attempt) + rand(2)) # caller must retry: the client does not retry 429\n  retry\nend","preventionTips":["Remember SimpleFin's client does NOT retry 429s (unlike Redbark) — always add caller-side backoff","Serialize per-bridge syncs and space schedules minutes apart","Log the bridge's 429 body; it often states the window you must wait"],"tags":["simplefin","http-429","rate-limit","throttling","backoff"],"backgroundTag":"http-429-rate-limited","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}