we-promise/sure · error · Provider::Mercury::MercuryError
not_found
not_found
Error message
Resource not found
What it means
Raised by Provider::Mercury#handle_response when Mercury returns HTTP 404, meaning the requested resource does not exist on Mercury's side. In this client it can only come from get_account or get_account_transactions, where the account ID is interpolated into the URL path (URL-encoded).
Source
Thrown at app/models/provider/mercury.rb:124
"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 "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."View on GitHub (pinned to e69894adb9)
Solutions
- Call get_accounts and verify the account_id is still in the returned list; if missing, remove or deactivate the local record for it.
- Check the stored ID for whitespace/newlines and exact UUID formatting before passing it.
- Confirm you used the API account id field (not the dashboard display ID or the last-4 digits).
- Verify base_url — a wrong base URL can turn a valid path into a 404.
Example fix
# before
provider.get_account_transactions(account_id, start_date: 30.days.ago)
# after
ids = provider.get_accounts[:accounts].map { |a| a[:id] }
if ids.include?(account_id)
provider.get_account_transactions(account_id, start_date: 30.days.ago)
else
Rails.logger.info "Mercury account #{account_id} no longer exists; skipping"
end Defensive patterns
Strategy: validation
Validate before calling
account_id = account_id.to_s.strip
unless account_id.match?(/\A[0-9a-f-]{36}\z/i) # Mercury UUID
raise ArgumentError, "not a Mercury account id: #{account_id.inspect}"
end Try / catch
begin
provider.get_account_transactions(account_id, start_date: start)
rescue Provider::Mercury::MercuryError => e
if e.error_type == :not_found
account.disable!(reason: "missing_at_provider")
else
raise
end
end Prevention
- Validate ID shape (UUID, no whitespace) before interpolating into a path.
- Reconcile cached account IDs against get_accounts each sync and retire the missing ones.
When it happens
Trigger: get_account(account_id) with an ID that was deleted or never existed; get_account_transactions with a typo'd or truncated UUID; an account closed at Mercury after you cached its ID; a base_url with a typo changing the effective path.
Common situations: Cached/stale account IDs synced earlier and the account was later closed; ID truncated by a CSV import or manual entry; copy/paste of an internal ID from Mercury's web dashboard that is not the API account UUID; trailing newline in the stored ID (partially mitigated by ERB::Util.url_encode, which will encode it into a non-matching path).
Related errors
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/ea21507d7de60908.
Report an issue: GitHub.