{"record":{"id":"da2cf8f7a03b82d2","repo":"we-promise/sure","slug":"bad-request-da2cf8","errorCode":"bad_request","errorMessage":"Bad request to Enable Banking API: #{response.body}","messagePattern":"Bad request to Enable Banking API: #(.+?)","errorType":"exception","errorClass":"EnableBankingError","httpStatus":400,"severity":"error","filePath":"app/models/provider/enable_banking.rb","lineNumber":285,"sourceCode":"      JWT.encode(payload, private_key, \"RS256\", header)\n    end\n\n    def auth_headers\n      {\n        \"Authorization\" => \"Bearer #{generate_jwt}\",\n        \"Accept\" => \"application/json\"\n      }\n    end\n\n    def handle_response(response)\n      case response.code\n      when 200, 201\n        parse_response_body(response)\n      when 204\n        {}\n      when 400\n        response_data = parse_error_response_body(response)\n        raise EnableBankingError.new(\"Bad request to Enable Banking API: #{response.body}\", :bad_request, response_data: response_data)\n      when 401\n        raise EnableBankingError.new(\"Invalid credentials or expired JWT\", :unauthorized)\n      when 403\n        raise EnableBankingError.new(\"Access forbidden - check your application permissions\", :access_forbidden)\n      when 404\n        raise EnableBankingError.new(\"Resource not found\", :not_found)\n      when 408\n        raise EnableBankingError.new(\"Request timeout from Enable Banking API\", :timeout)\n      when 422\n        response_data = parse_response_body(response)\n        raise EnableBankingError.new(\"Validation error from Enable Banking API: #{response.body}\", :validation_error, response_data: response_data)\n      when 429\n        raise EnableBankingError.new(\"Rate limit exceeded. Please try again later.\", :rate_limited)\n      else\n        response_data = parse_error_response_body(response)\n        raise EnableBankingError.new(\"Failed to fetch data: #{response.code} #{response.message} - #{response.body}\", :fetch_failed, response_data: response_data)\n      end\n    end","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/enable_banking.rb#L267-L303","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"# before\nbanks = client.get_aspsps(country: params[:country]) # \"de\", \"\" , \"Germany\" all leak to API\n\n# after\ncountry = params[:country].to_s.upcase\nraise ArgumentError, \"country must be ISO 3166-1 alpha-2\" unless country.match?(/\\A[A-Z]{2}\\z/)\nbanks = client.get_aspsps(country: country)","handlingStrategy":"validation","validationCode":"def valid_iso_country?(value)\n  value.to_s.match?(/\\A[A-Z]{2}\\z/)\nend\n\nASPSPS_CACHE = Rails.cache\n\ndef aspsp_exists?(client, name:, country:)\n  list = ASPSPS_CACHE.fetch(\"eb_aspsps_#{country}\", expires_in: 1.hour) do\n    client.get_aspsps(country: country)\n  end\n  list.any? { |a| a[:name] == name }\nend","typeGuard":"def eb_bad_request?(error)\n  error.is_a?(Provider::EnableBanking::EnableBankingError) && error.error_type == :bad_request\nend","tryCatchPattern":"begin\n  client.start_authorization(aspsp_name: name, aspsp_country: country, redirect_url: url)\nrescue Provider::EnableBanking::EnableBankingError => e\n  raise unless e.error_type == :bad_request\n  Rails.logger.error(\"EB 400: #{e.response_data.inspect}\") # exact API error + detail\n  raise\nend","preventionTips":["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"],"tags":["enable-banking","http-400","validation","api"],"backgroundTag":"http-400-bad-request","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}