we-promise/sure · error · Provider::Mercury::MercuryError
fetch_failed
fetch_failed
Error message
Failed to fetch data: #{response.code} #{response.message} - #{response.body} What it means
The catch-all branch of Provider::Mercury#handle_response: Mercury returned a status other than 200/400/401/403/404/429 (most commonly 5xx, but also unexpected 3xx or odd 4xx). The message interpolates the raw code, HTTParty message and body, and the code/body are also written to Rails.logger.error before raising, so the log line "Mercury API: Unexpected response" is the searchable breadcrumb.
Source
Thrown at app/models/provider/mercury.rb:129
case response.code
when 200
JSON.parse(response.body, symbolize_names: true)
when 400
Rails.logger.error "Mercury API: Bad request - #{response.body}"
raise MercuryError.new("Bad request to Mercury API: #{response.body}", :bad_request)
when 401
# Parse the error response for more specific messages
error_message = parse_error_message(response.body)
raise MercuryError.new(error_message, :unauthorized)
when 403
raise MercuryError.new("Access forbidden - check your API token permissions", :access_forbidden)
when 404
raise MercuryError.new("Resource not found", :not_found)
when 429
raise MercuryError.new("Rate limit exceeded. Please try again later.", :rate_limited)
else
Rails.logger.error "Mercury API: Unexpected response - Code: #{response.code}, Body: #{response.body}"
raise MercuryError.new("Failed to fetch data: #{response.code} #{response.message} - #{response.body}", :fetch_failed)
end
end
def parse_error_message(body)
parsed = JSON.parse(body, symbolize_names: true)
errors = parsed[:errors] || {}
case errors[:errorCode]
when "ipNotWhitelisted"
ip = errors[:ip] || "unknown"
"IP address not whitelisted (#{ip}). Add your IP to the API token's whitelist in Mercury dashboard."
when "noTokenInDBButMaybeMalformed"
"Invalid token format. Make sure to include the 'secret-token:' prefix."
else
errors[:message] || "Invalid API token"
end
rescue JSON::ParserError
"Invalid API token"View on GitHub (pinned to e69894adb9)
Solutions
- Check the logged body — grep the app log for 'Mercury API: Unexpected response' to get the exact code and upstream message.
- Check Mercury status/dashboard for an ongoing incident if the code is 5xx, then retry with backoff after a few minutes.
- If 410/405-style codes appear, compare the current Mercury API docs against the path in base_url — the endpoint may have moved.
- Narrow the request (smaller date range or pagination) if 504s repeat on heavy queries.
- Treat as transient first: this error_type is :fetch_failed, so retry once or twice before surfacing to the user.
Example fix
# before result = provider.get_accounts # after begin result = provider.get_accounts rescue Provider::Mercury::MercuryError => e raise if e.error_type != :fetch_failed || attempt >= 2 attempt += 1 sleep(30 * attempt) retry end
Defensive patterns
Strategy: try-catch
Try / catch
begin
provider.get_accounts
rescue Provider::Mercury::MercuryError => e
if e.error_type == :fetch_failed && attempts < 2 # often 5xx/transient
attempts += 1
sleep(60)
retry
end
raise
end Prevention
- Watch the paired 'Mercury API: Unexpected response' log line for the real status before choosing a response.
- Keep date windows modest in get_account_transactions to avoid 504-prone heavy queries.
- Alert on fetch_failed spikes to catch Mercury incidents early.
When it happens
Trigger: Mercury outage returning 500/502/503; a gateway timeout 504 on a long transactions query with a huge date range; an unexpected 410/422 for a deprecated endpoint; HTTParty returning a non-listed code after following redirects.
Common situations: Incidents on api.mercury.com; very wide date ranges in get_account_transactions straining upstream; a new Mercury API version returning 410 Gone on the old path; proxies or corporate gateways injecting 502s.
Related errors
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/805236b02f0a9ed8.
Report an issue: GitHub.