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 defaults

View on GitHub (pinned to e69894adb9)

Solutions

  1. Read the response body in the exception — the bridge usually names the bad parameter
  2. Pass Date/Time objects for start_date/end_date and let the client convert to timestamps
  3. Re-verify the stored access URL is the full claimed URL including credentials
  4. 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

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


AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21). Data as JSON: /api/errors/ff7685970921dc02. Report an issue: GitHub.