we-promise/sure · error · EnableBankingError
timeout
timeout
Error message
Request timeout from Enable Banking API
What it means
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.
Source
Thrown at app/models/provider/enable_banking.rb:293
end
def handle_response(response)
case response.code
when 200, 201
parse_response_body(response)
when 204
{}
when 400
response_data = parse_error_response_body(response)
raise EnableBankingError.new("Bad request to Enable Banking API: #{response.body}", :bad_request, response_data: response_data)
when 401
raise EnableBankingError.new("Invalid credentials or expired JWT", :unauthorized)
when 403
raise EnableBankingError.new("Access forbidden - check your application permissions", :access_forbidden)
when 404
raise EnableBankingError.new("Resource not found", :not_found)
when 408
raise EnableBankingError.new("Request timeout from Enable Banking API", :timeout)
when 422
response_data = parse_response_body(response)
raise EnableBankingError.new("Validation error from Enable Banking API: #{response.body}", :validation_error, response_data: response_data)
when 429
raise EnableBankingError.new("Rate limit exceeded. Please try again later.", :rate_limited)
else
response_data = parse_error_response_body(response)
raise EnableBankingError.new("Failed to fetch data: #{response.code} #{response.message} - #{response.body}", :fetch_failed, response_data: response_data)
end
end
def parse_error_response_body(response)
return {} if response.body.blank?
JSON.parse(response.body, symbolize_names: true)
rescue JSON::ParserError
{ raw_body: response.body.to_s }
endView on GitHub (pinned to e69894adb9)
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
Example fix
// before provider.get_account_transactions(account_id: id, date_from: 3.years.ago, date_to: Date.current) // after begin provider.get_account_transactions(account_id: id, date_from: date_from, date_to: date_to) rescue Provider::EnableBanking::EnableBankingError => e raise if e.error_type != :timeout RetryableSync.schedule(account, wait: 2.minutes) # retry next cycle with smaller range end
Defensive patterns
Strategy: retry
Type guard
def enable_banking_timeout?(error) error.is_a?(Provider::EnableBanking::EnableBankingError) && error.error_type == :timeout end
Try / catch
begin
provider.get_account_transactions(account_id: id, date_from: from, date_to: to)
rescue Provider::EnableBanking::EnableBankingError => e
if e.error_type == :timeout
RetryableSync.schedule(account, wait: 2.minutes) # transient upstream slowness
else
raise
end
end Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/e0feaa68840a8f0d.
Report an issue: GitHub.