we-promise/sure · error · LunchflowError
bad_request
bad_request
Error message
Bad request to Lunch Flow API: #{response.body} What it means
HTTP 400 branch of Provider::Lunchflow#handle_response (app/models/provider/lunchflow.rb:125-127). Thrown as LunchflowError(:bad_request) whenever the Lunchflow API answers GET endpoints with status 400; the raw response body is embedded in both the Rails log ('Lunch Flow API: Bad request - ...') and the error message, so the server-side validation message is visible.
Source
Thrown at app/models/provider/lunchflow.rb:127
end
private
def auth_headers
{
"x-api-key" => api_key,
"Content-Type" => "application/json",
"Accept" => "application/json"
}
end
def handle_response(response)
case response.code
when 200
JSON.parse(response.body, symbolize_names: true)
when 400
Rails.logger.error "Lunch Flow API: Bad request - #{response.body}"
raise LunchflowError.new("Bad request to Lunch Flow API: #{response.body}", :bad_request)
when 401
raise LunchflowError.new("Invalid API key", :unauthorized)
when 403
raise LunchflowError.new("Access forbidden - check your API key permissions", :access_forbidden)
when 404
raise LunchflowError.new("Resource not found", :not_found)
when 429
raise LunchflowError.new("Rate limit exceeded. Please try again later.", :rate_limited)
else
Rails.logger.error "Lunch Flow API: Unexpected response - Code: #{response.code}, Body: #{response.body}"
raise LunchflowError.new("Failed to fetch data: #{response.code} #{response.message} - #{response.body}", :fetch_failed)
end
end
class LunchflowError < StandardError
attr_reader :error_type
def initialize(message, error_type = :unknown)View on GitHub (pinned to e69894adb9)
Solutions
- Read the embedded response body in the error message and the 'Lunch Flow API: Bad request' log line — Lunchflow states exactly which parameter failed.
- Echo the exact request (path + query string from logs) with curl and adjust the parameters until the server accepts them.
- Normalize inputs before calling: Date objects for start/end, trimmed account_id strings, and omit params you do not need.
- If the body mentions an unknown parameter, check whether a base_url override points to an outdated API version.
Example fix
// before
client.get_account_transactions(acct_id, start_date: from, end_date: to, include_pending: true)
// after (send only supported, validated params)
params = { start_date: from.to_date.to_s, end_date: to.to_date.to_s }
client.get_account_transactions(acct_id.to_s.strip, **params.reject { |_, v| v.blank? }) Defensive patterns
Strategy: validation
Validate before calling
raise ArgumentError, 'account_id required' if acct_id.blank?
raise ArgumentError, 'start after end' if from && to && from > to
params = { start_date: from&.to_date, end_date: to&.to_date }.compact Type guard
def bad_request?(e) e.is_a?(Provider::Lunchflow::LunchflowError) && e.error_type == :bad_request end
Try / catch
begin client.get_account_transactions(acct_id, **params) rescue Provider::Lunchflow::LunchflowError => e raise unless e.error_type == :bad_request log_to_debug_entries(e.message) # body contains the server's reason skip_account(acct_id) end
Prevention
- Send only parameters the endpoint documents; omit nils.
- Always pass Date objects and let the client serialize.
- Never retry a 400 — fix the request shape instead.
When it happens
Trigger: GET /accounts/:id/transactions or /holdings with query parameters the server rejects: unsupported parameter combinations (e.g. include_pending=true on an endpoint that ignores/rejects it), a date range the server deems too wide, or an account id segment that fails server-side validation even after ERB::Util.url_encode. Since the client coerces dates with #to_date, malformed dates usually fail client-side first (surfacing as :request_failed instead), so a 400 here is genuine server-side validation.
Common situations: Provider API version change tightening parameter validation (base_url pinned to an old /api/v1 path); syncing with parameter combos that worked in sandbox but not production; account ids copied from another provider or environment.
Related errors
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/ac6cd82b1495883b.
Report an issue: GitHub.