{"record":{"id":"ff7685970921dc02","repo":"we-promise/sure","slug":"bad-request-ff7685","errorCode":"bad_request","errorMessage":"Bad request to SimpleFin API: #{response.body}","messagePattern":"Bad request to SimpleFin API: #(.+?)","errorType":"exception","errorClass":"Provider::Simplefin::SimplefinError","httpStatus":400,"severity":"error","filePath":"app/models/provider/simplefin.rb","lineNumber":90,"sourceCode":"    # spec-compliant way to exclude pending is to omit the param entirely.\n    query_params[\"pending\"] = \"1\" if pending\n\n    accounts_url = \"#{access_url}/accounts\"\n    accounts_url += \"?#{URI.encode_www_form(query_params)}\" unless query_params.empty?\n\n    # The access URL already contains HTTP Basic Auth credentials\n    # 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","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/simplefin.rb#L72-L108","documentation":"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.","triggerScenarios":"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.","commonSituations":"Date objects in odd formats stringifying badly; access URL truncated at persistence time (column length limit); bridge firmware tightening param validation.","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"],"exampleFix":"# before\nclient.get_accounts(url, start_date: \"2024-01-01 00:00:00\")\n\n# after\nclient.get_accounts(url, start_date: Date.new(2024, 1, 1), end_date: Date.new(2024, 12, 31))","handlingStrategy":"validation","validationCode":"def simplefin_dates_valid?(start_date, end_date)\n  [ start_date, end_date ].compact.all? { |d| d.respond_to?(:to_time) } &&\n    (start_date.nil? || end_date.nil? || start_date <= end_date)\nend","typeGuard":"def simplefin_bad_request?(error)\n  error.is_a?(Provider::Simplefin::SimplefinError) && error.error_type == :bad_request\nend","tryCatchPattern":"begin\n  client.get_accounts(access_url, start_date: from, end_date: to)\nrescue Provider::Simplefin::SimplefinError => e\n  raise unless e.error_type == :bad_request\n  Rails.logger.error(\"SimpleFin 400 body: #{e.message}\") # message embeds raw body\n  raise\nend","preventionTips":["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"],"tags":["simplefin","http-400","bad-request","query-params","unix-timestamp"],"backgroundTag":"http-400-bad-request","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}