we-promise/sure · error · EnableBankingError
bad_request
bad_request
Error message
Bad request to Enable Banking API: #{response.body} What it means
Raised by Provider::EnableBanking#handle_response on HTTP 400: Enable Banking rejected the request payload as invalid. The full response body is embedded in the message and the parsed JSON is available as error.response_data (falling back to { raw_body: ... } when the body is not JSON), so the exact API error message and details are inspectable.
Source
Thrown at app/models/provider/enable_banking.rb:285
JWT.encode(payload, private_key, "RS256", header)
end
def auth_headers
{
"Authorization" => "Bearer #{generate_jwt}",
"Accept" => "application/json"
}
end
def handle_response(response)
case response.code
when 200, 201
parse_response_body(response)
when 204
{}
when 400
response_data = parse_error_response_body(response)
raise EnableBankingError.new("Bad request to Enable Banking API: #{response.body}", :bad_request, response_data: response_data)
when 401
raise EnableBankingError.new("Invalid credentials or expired JWT", :unauthorized)
when 403
raise EnableBankingError.new("Access forbidden - check your application permissions", :access_forbidden)
when 404
raise EnableBankingError.new("Resource not found", :not_found)
when 408
raise EnableBankingError.new("Request timeout from Enable Banking API", :timeout)
when 422
response_data = parse_response_body(response)
raise EnableBankingError.new("Validation error from Enable Banking API: #{response.body}", :validation_error, response_data: response_data)
when 429
raise EnableBankingError.new("Rate limit exceeded. Please try again later.", :rate_limited)
else
response_data = parse_error_response_body(response)
raise EnableBankingError.new("Failed to fetch data: #{response.code} #{response.message} - #{response.body}", :fetch_failed, response_data: response_data)
end
endView on GitHub (pinned to e69894adb9)
Solutions
- Inspect e.response_data (or the body embedded in the message) — Enable Banking returns a specific error string and detail hash
- For auth calls, verify aspsp_name/aspsp_country exactly match an entry from get_aspsps(country:) and psu_type is 'personal' or 'business'
- For create_session 400s, stop retrying the code (single-use) and restart the authorization flow
- Validate country with an ISO 3166-1 alpha-2 check before calling get_aspsps
Example fix
# before
banks = client.get_aspsps(country: params[:country]) # "de", "" , "Germany" all leak to API
# after
country = params[:country].to_s.upcase
raise ArgumentError, "country must be ISO 3166-1 alpha-2" unless country.match?(/\A[A-Z]{2}\z/)
banks = client.get_aspsps(country: country) Defensive patterns
Strategy: validation
Validate before calling
def valid_iso_country?(value)
value.to_s.match?(/\A[A-Z]{2}\z/)
end
ASPSPS_CACHE = Rails.cache
def aspsp_exists?(client, name:, country:)
list = ASPSPS_CACHE.fetch("eb_aspsps_#{country}", expires_in: 1.hour) do
client.get_aspsps(country: country)
end
list.any? { |a| a[:name] == name }
end Type guard
def eb_bad_request?(error) error.is_a?(Provider::EnableBanking::EnableBankingError) && error.error_type == :bad_request end
Try / catch
begin
client.start_authorization(aspsp_name: name, aspsp_country: country, redirect_url: url)
rescue Provider::EnableBanking::EnableBankingError => e
raise unless e.error_type == :bad_request
Rails.logger.error("EB 400: #{e.response_data.inspect}") # exact API error + detail
raise
end Prevention
- Validate country (ISO 3166-1 alpha-2, uppercase) before calling get_aspsps
- Source aspsp_name/aspsp_country from cached get_aspsps output, never free text
- Never retry create_session on 400 — the code is likely consumed; restart the flow
- Always log e.response_data; Enable Banking's error body names the offending field
When it happens
Trigger: get_aspsps with a non-ISO-3166 country value; start_authorization with an aspsp name/country pair that doesn't exist in get_aspsps output, an invalid psu_type, or a malformed redirect_url; create_session with an already-used or expired code (codes are single-use).
Common situations: Hardcoded or user-typed bank names drifting from the ASPSP catalog, country codes lowercased or misspelled, retrying a consumed auth code after a network hiccup, redirect_url not matching the registered one.
Related errors
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/da2cf8f7a03b82d2.
Report an issue: GitHub.