{"record":{"id":"0c69d0c3d0b3173b","repo":"we-promise/sure","slug":"coinstats-api-request-failed-e-message","errorCode":null,"errorMessage":"CoinStats API request failed: #{e.message}","messagePattern":"CoinStats API request failed: #(.+?)","errorType":"exception","errorClass":"Provider::Coinstats::Error","httpStatus":null,"severity":"warning","filePath":"app/models/provider/coinstats.rb","lineNumber":45,"sourceCode":"  default_options.merge!({ timeout: 120 }.merge(httparty_ssl_options))\n\n  attr_reader :api_key\n\n  # @param api_key [String] CoinStats API key for authentication\n  def initialize(api_key)\n    @api_key = api_key\n  end\n\n  # Get the list of blockchains supported by CoinStats\n  # https://coinstats.app/api-docs/openapi/get-blockchains\n  def get_blockchains\n    with_provider_response do\n      res = self.class.get(\"#{BASE_URL}/wallet/blockchains\", headers: auth_headers)\n      handle_response(res)\n    end\n  rescue SocketError, Net::OpenTimeout, Net::ReadTimeout => e\n    Rails.logger.error \"CoinStats API: GET /wallet/blockchains failed: #{e.class}: #{e.message}\"\n    raise Error, \"CoinStats API request failed: #{e.message}\"\n  end\n\n  # Returns blockchain options formatted for select dropdowns\n  # @return [Array<Array>] Array of [label, value] pairs sorted alphabetically\n  def blockchain_options\n    response = get_blockchains\n\n    unless response.success?\n      Rails.logger.warn(\"CoinStats: failed to fetch blockchains: #{response.error&.message}\")\n      return []\n    end\n\n    raw_blockchains = response.data\n    items = if raw_blockchains.is_a?(Array)\n      raw_blockchains\n    elsif raw_blockchains.respond_to?(:dig) && raw_blockchains[:data].is_a?(Array)\n      raw_blockchains[:data]\n    else","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/coinstats.rb#L27-L63","documentation":"Raised by Provider::CoinStats#get_blockchains when the underlying HTTP GET /wallet/blockchains fails with SocketError, Net::OpenTimeout, or Net::ReadTimeout — i.e. the request never completed at the network layer. The original exception is logged (\"CoinStats: GET /wallet/blockchains failed: <class>: <msg>\") and re-raised as Provider::CoinStats::Error with \"CoinStats API request failed: #{e.message}\".","triggerScenarios":"DNS resolution failure for the CoinStats host (SocketError: \"getaddrinfo: Name or service not known\"); TCP connect timeout (Net::OpenTimeout) when api.coinstats.app is unreachable or blocked by a firewall; Net::ReadTimeout (\"execution expired\") when the server accepts the connection but never responds within the HTTParty timeout — e.g. during a CoinStats outage or from a very slow proxy/VPN egress.","commonSituations":"Containers/servers with broken or flaky resolvers; corporate proxies blocking unknown API hosts; mobile/VPN networks dropping long-lived connections; CoinStats-side incidents; cold-start DNS lookups failing in short-lived serverless-style processes; calling blockchain_options during setup when networking is not yet healthy.","solutions":["Check the log line for the wrapped class: SocketError = DNS, OpenTimeout = connect, ReadTimeout = server slow/unresponsive — they have different fixes.","Verify basic reachability from the host: curl -I https://api.coinstats.app (fix DNS or firewall if this fails).","If connect/read timeouts recur, pass an explicit timeout to the HTTParty call (e.g. timeout: 30) rather than defaults, and fail fast.","Retry with backoff for transient network blips; the method is a read (GET), so retries are safe.","If it persists only for one host, suspect proxy/egress rules; if it persists for everyone, check CoinStats status and defer the call."],"exampleFix":"# before\nres = self.class.get(\"#{BASE_URL}/wallet/blockchains\", headers: auth_headers)\n\n# after\nres = self.class.get(\"#{BASE_URL}/wallet/blockchains\", headers: auth_headers, timeout: 30)\n# and at the call site:\nbegin\n  options = provider.blockchain_options\nrescue Provider::CoinStats::Error => e\n  Rails.logger.warn(\"CoinStats blockchains unavailable: #{e.message}\")\n  options = []\nend","handlingStrategy":"retry","validationCode":"require \"socket\"\nTCPSocket.new(\"api.coinstats.app\", 443).close # fail fast if egress/DNS is broken before sync","typeGuard":"def coinstats_network_error?(err)\n  err.is_a?(Provider::CoinStats::Error) && err.message.start_with?(\"CoinStats API request failed\")\nend","tryCatchPattern":"attempts = 0\nbegin\n  attempts += 1\n  options = provider.blockchain_options\nrescue Provider::CoinStats::Error => e\n  raise if attempts >= 3 || e.message.include?(\"getaddrinfo\")\n  sleep(2**attempts)\n  retry\nelse\n  options ||= []\nend","preventionTips":["Add an explicit timeout: option to CoinStats HTTParty calls instead of relying on defaults.","Ensure the host can resolve and reach api.coinstats.app (DNS, proxy, firewall) before scheduling syncs."," blockchain_options already degrades to [] — keep call sites rendering empty-with-warning rather than raising.","Retry only transient classes: ReadTimeout/OpenTimeout; fix DNS first for SocketError."],"tags":["coinstats","network","timeout","dns","defi-wallet"],"backgroundTag":"network-timeout","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}