{"record":{"id":"e0feaa68840a8f0d","repo":"we-promise/sure","slug":"timeout","errorCode":"timeout","errorMessage":"Request timeout from Enable Banking API","messagePattern":"Request timeout from Enable Banking API","errorType":"exception","errorClass":"EnableBankingError","httpStatus":408,"severity":"error","filePath":"app/models/provider/enable_banking.rb","lineNumber":293,"sourceCode":"    end\n\n    def handle_response(response)\n      case response.code\n      when 200, 201\n        parse_response_body(response)\n      when 204\n        {}\n      when 400\n        response_data = parse_error_response_body(response)\n        raise EnableBankingError.new(\"Bad request to Enable Banking API: #{response.body}\", :bad_request, response_data: response_data)\n      when 401\n        raise EnableBankingError.new(\"Invalid credentials or expired JWT\", :unauthorized)\n      when 403\n        raise EnableBankingError.new(\"Access forbidden - check your application permissions\", :access_forbidden)\n      when 404\n        raise EnableBankingError.new(\"Resource not found\", :not_found)\n      when 408\n        raise EnableBankingError.new(\"Request timeout from Enable Banking API\", :timeout)\n      when 422\n        response_data = parse_response_body(response)\n        raise EnableBankingError.new(\"Validation error from Enable Banking API: #{response.body}\", :validation_error, response_data: response_data)\n      when 429\n        raise EnableBankingError.new(\"Rate limit exceeded. Please try again later.\", :rate_limited)\n      else\n        response_data = parse_error_response_body(response)\n        raise EnableBankingError.new(\"Failed to fetch data: #{response.code} #{response.message} - #{response.body}\", :fetch_failed, response_data: response_data)\n      end\n    end\n\n    def parse_error_response_body(response)\n      return {} if response.body.blank?\n\n      JSON.parse(response.body, symbolize_names: true)\n    rescue JSON::ParserError\n      { raw_body: response.body.to_s }\n    end","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/enable_banking.rb#L275-L311","documentation":"Raised by Provider::EnableBanking#handle_response when https://api.enablebanking.com returns HTTP 408. Enable Banking is an open-banking aggregator that proxies each request to the user's bank (ASPSP); a 408 means the upstream bank or the aggregator gateway abandoned the request server-side before responding. It is not the local HTTParty timeout (120s) - that surfaces as :request_failed via the SocketError/Net timeout rescues.","triggerScenarios":"GET /accounts/{id}/transactions with a wide date_from/date_to range against a slow ASPSP; the first history sync of a newly connected account; calls made while the target bank is in a maintenance window; hammering the same session with concurrent sync jobs so the gateway queues and times out.","commonSituations":"Initial sync pulling years of transactions in one call, one chronically slow bank among a user's connections, transient aggregator congestion, or dev and prod environments sharing one application_id and exhausting gateway capacity simultaneously.","solutions":["Retry the same call on the next sync cycle or after short backoff (e.g. 30s, 2m) - most 408s are transient upstream slowness, not a config problem","Narrow the transactions window: fetch in smaller date ranges and page with continuation_key instead of one huge request","If only one institution fails, check the Enable Banking status/health endpoint and that ASPSP's availability before touching credentials","Branch on error_type == :timeout in the caller so the sync is retried later rather than flagged as an auth or consent failure"],"exampleFix":"// before\nprovider.get_account_transactions(account_id: id, date_from: 3.years.ago, date_to: Date.current)\n\n// after\nbegin\n  provider.get_account_transactions(account_id: id, date_from: date_from, date_to: date_to)\nrescue Provider::EnableBanking::EnableBankingError => e\n  raise if e.error_type != :timeout\n  RetryableSync.schedule(account, wait: 2.minutes) # retry next cycle with smaller range\nend","handlingStrategy":"retry","validationCode":null,"typeGuard":"def enable_banking_timeout?(error)\n  error.is_a?(Provider::EnableBanking::EnableBankingError) && error.error_type == :timeout\nend","tryCatchPattern":"begin\n  provider.get_account_transactions(account_id: id, date_from: from, date_to: to)\nrescue Provider::EnableBanking::EnableBankingError => e\n  if e.error_type == :timeout\n    RetryableSync.schedule(account, wait: 2.minutes) # transient upstream slowness\n  else\n    raise\n  end\nend","preventionTips":["Fetch transactions in date-range chunks (e.g. 30-89 days) instead of one full-history call","Stagger per-bank syncs so one slow ASPSP cannot pile up concurrent 408s","Monitor per-institution 408 rates to spot a single degraded bank","Never treat :timeout as a credential/consent failure in sync bookkeeping"],"tags":["enable-banking","http-408","timeout","open-banking","aspssp","transient"],"backgroundTag":"request-timeout","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}