{"record":{"id":"2d9a2ebfd9537872","repo":"we-promise/sure","slug":"api-error-response-code-2d9a2e","errorCode":null,"errorMessage":"API error: #{response.code}","messagePattern":"API error: #(.+?)","errorType":"exception","errorClass":"Provider::Coinbase::ApiError","httpStatus":null,"severity":"error","filePath":"app/models/provider/coinbase.rb","lineNumber":206,"sourceCode":"        \"Authorization\" => \"Bearer #{generate_jwt(method, path)}\",\n        \"Content-Type\" => \"application/json\"\n      }\n    end\n\n    def handle_response(response)\n      parsed = response.parsed_response\n\n      case response.code\n      when 200..299\n        parsed.is_a?(Hash) ? parsed : { \"data\" => parsed }\n      when 401\n        error_msg = extract_error_message(parsed) || \"Unauthorized - check your API key and secret\"\n        raise AuthenticationError, error_msg\n      when 429\n        raise RateLimitError, \"Rate limit exceeded\"\n      else\n        error_msg = extract_error_message(parsed) || \"API error: #{response.code}\"\n        raise ApiError, error_msg\n      end\n    end\n\n    def extract_error_message(parsed)\n      return parsed if parsed.is_a?(String)\n      return nil unless parsed.is_a?(Hash)\n\n      parsed.dig(\"errors\", 0, \"message\") || parsed[\"error\"] || parsed[\"message\"]\n    end\nend\n","sourceCodeStart":188,"sourceCodeEnd":217,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/coinbase.rb#L188-L217","documentation":"Raised by Provider::Coinbase#handle_response (coinbase.rb:206) as Provider::Coinbase::ApiError for any non-2xx status that is not 401 or 429. The message is Coinbase's own error text (parsed.dig(\"errors\", 0, \"message\") || parsed[\"error\"] || parsed[\"message\"]) or the literal \"API error: #{response.code}\" when the body yields nothing parseable.","triggerScenarios":"HTTP 400 for invalid request params (bad pagination cursor, unknown account id, malformed body); HTTP 404 for deleted resources; HTTP 500/503 during Coinbase incidents; HTML/JSON-parse failures from proxies that leave parsed nil so the generic \"API error: <code>\" shows; version changes where an endpoint moves or changes shape.","commonSituations":"Coinbase API version drift after an announcement (fields renamed, endpoints deprecated); passing an account id from a different key's scope; deploys behind a corporate proxy that rewrites error bodies; incident-window syncs returning 503s that a job retries too aggressively.","solutions":["Read the message text: a Coinbase error string points at the exact bad param/resource; \"API error: <code>\" means the body was unparseable — capture the raw response body to diagnose.","Fix the offending request parameter/resource id for 4xx errors.","Retry 5xx with backoff and check the Coinbase status page during suspected incidents.","Pin/request the correct API version in headers if a behavior change is suspected.","Isolate per-resource failures so one 4xx does not abort a whole import."],"exampleFix":"# before\nbegin\n  txs = provider.get_transactions(account_id: id)\nrescue => e\n  raise # anything kills the sync\nend\n\n# after\nbegin\n  txs = provider.get_transactions(account_id: id)\nrescue Provider::Coinbase::ApiError => e\n  if /5\\d\\d/.match?(e.message)\n    raise RetryableJobError, e.message\n  else\n    Rails.logger.error(\"Coinbase call for #{id} failed: #{e.message}\")\n  end\nend","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"def coinbase_api_error?(err)\n  err.is_a?(Provider::Coinbase::ApiError)\nend","tryCatchPattern":"begin\n  txs = provider.get_transactions(account_id: id)\nrescue Provider::Coinbase::ApiError => e\n  if /API error: 5/.match?(e.message)\n    raise RetryableJobError, e.message\n  else\n    Rails.logger.error(\"Coinbase failure for #{id}: #{e.message}\")\n  end\nend","preventionTips":["When the message is the generic \"API error: <code>\", capture the raw body — the JSON error path yielded nothing.","Treat 5xx as retryable and 4xx as permanent in job retry policy.","Pin the Coinbase API version in request headers to survive behavior drift.","Scope account ids to the same key that fetched them."],"tags":["coinbase","http-error","api-error","catch-all","crypto"],"backgroundTag":"api-request-failed","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}