we-promise/sure · error · Provider::Simplefin::SimplefinError
bad_request
bad_request
Error message
Bad request to SimpleFin API: #{response.body} What it means
Raised by Provider::Simplefin#get_accounts when GET {access_url}/accounts returns 400, with error_type :bad_request. Unlike the Redbark client, this message embeds the raw response body, so the provider's error detail is visible. Most often caused by invalid query params — start-date/end-date must be Unix timestamp strings (the client builds them via to_time.to_i), and a corrupted access URL produces malformed requests.
Source
Thrown at app/models/provider/simplefin.rb:90
# spec-compliant way to exclude pending is to omit the param entirely.
query_params["pending"] = "1" if pending
accounts_url = "#{access_url}/accounts"
accounts_url += "?#{URI.encode_www_form(query_params)}" unless query_params.empty?
# The access URL already contains HTTP Basic Auth credentials
# Use retry logic with exponential backoff for transient network failures
# Use self.class.get to inherit class-level SSL and timeout defaults
response = with_retries("GET /accounts") do
self.class.get(accounts_url)
end
case response.code
when 200
JSON.parse(response.body, symbolize_names: true)
when 400
Rails.logger.error "SimpleFin API: Bad request - #{response.body}"
raise SimplefinError.new("Bad request to SimpleFin API: #{response.body}", :bad_request)
when 403
raise SimplefinError.new("Access URL is no longer valid", :access_forbidden)
when 402
raise SimplefinError.new("Payment required to access this account", :payment_required)
when 429
Rails.logger.warn "SimpleFin API: Rate limited - #{response.body}"
raise SimplefinError.new("SimpleFin rate limit exceeded. Please try again later.", :rate_limited)
when 500..599
Rails.logger.error "SimpleFin API: Server error - Code: #{response.code}, Body: #{response.body}"
raise SimplefinError.new("SimpleFin server error (#{response.code}). Please try again later.", :server_error)
else
Rails.logger.error "SimpleFin API: Unexpected response - Code: #{response.code}, Body: #{response.body}"
raise SimplefinError.new("Failed to fetch accounts: #{response.code} #{response.message} - #{response.body}", :fetch_failed)
end
end
def get_info(base_url)
# Use self.class.get to inherit class-level SSL and timeout defaultsView on GitHub (pinned to e69894adb9)
Solutions
- Read the response body in the exception — the bridge usually names the bad parameter
- Pass Date/Time objects for start_date/end_date and let the client convert to timestamps
- Re-verify the stored access URL is the full claimed URL including credentials
- Omit optional params (pending) unless needed rather than sending blank values
Example fix
# before client.get_accounts(url, start_date: "2024-01-01 00:00:00") # after client.get_accounts(url, start_date: Date.new(2024, 1, 1), end_date: Date.new(2024, 12, 31))
Defensive patterns
Strategy: validation
Validate before calling
def simplefin_dates_valid?(start_date, end_date)
[ start_date, end_date ].compact.all? { |d| d.respond_to?(:to_time) } &&
(start_date.nil? || end_date.nil? || start_date <= end_date)
end Type guard
def simplefin_bad_request?(error) error.is_a?(Provider::Simplefin::SimplefinError) && error.error_type == :bad_request end
Try / catch
begin
client.get_accounts(access_url, start_date: from, end_date: to)
rescue Provider::Simplefin::SimplefinError => e
raise unless e.error_type == :bad_request
Rails.logger.error("SimpleFin 400 body: #{e.message}") # message embeds raw body
raise
end Prevention
- Pass Date/Time objects; let the client do the Unix timestamp conversion
- Store the full claimed access URL verbatim (no truncation) and re-verify before syncing
- Note this error message intentionally includes the raw body — keep it out of user-facing UI
When it happens
Trigger: Passing start_date/end_date values that don't convert to valid Unix timestamps; an access URL that lost its embedded credentials or query string so /accounts gets a malformed auth/query; unknown param rejected by the bridge.
Common situations: Date objects in odd formats stringifying badly; access URL truncated at persistence time (column length limit); bridge firmware tightening param validation.
Related errors
- bad_request
- bad_request
- Please provide a name and a complete US address (street, cit
- bad_request
- bad_request
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/ff7685970921dc02.
Report an issue: GitHub.