{"record":{"id":"0d8e9456db4efe58","repo":"instructure/canvas-lms","slug":"host-resolves-to-only-unparseable-ips","errorCode":null,"errorMessage":"#{host} resolves to only unparseable IPs...","messagePattern":"#(.+?) resolves to only unparseable IPs\\.\\.\\.","errorType":"exception","errorClass":"UnresolvableUriError","httpStatus":null,"severity":"error","filePath":"gems/canvas_http/lib/canvas_http.rb","lineNumber":287,"sourceCode":"    return false if blocked_ip_ranges.empty?\n\n    resolved_addrs = Resolv.getaddresses(host)\n    unless resolved_addrs.any?\n      # this is actually a different condition than the host being insecure,\n      # and having separate telemetry is helpful for understanding transient failures.\n      raise UnresolvableUriError, \"#{host} cannot be resolved to any address\"\n    end\n\n    ip_addrs = resolved_addrs.filter_map do |ip|\n      ::IPAddr.new(ip)\n    rescue IPAddr::InvalidAddressError\n      # this should never happen, Resolv should only be passing back IPs, but\n      # let's make sure we can see if the impossible occurs\n      logger.warn(\"CANVAS_HTTP WARNING | host: #{host} | invalid_ip: #{ip}\")\n      nil\n    end\n    unless ip_addrs.any?\n      raise UnresolvableUriError, \"#{host} resolves to only unparseable IPs...\"\n    end\n\n    blocked_ip_ranges.each do |range|\n      addr_range = ::IPAddr.new(range)\n      ip_addrs.any? do |addr|\n        if addr_range.include?(addr)\n          logger.warn(\"CANVAS_HTTP WARNING insecure address | host: #{host} | insecure_address: #{addr} | range: #{range}\")\n          return true\n        end\n      end\n    end\n    false\n  end\n\n  # returns a Net::HTTP connection object for the given URI object\n  def self.connection_for_uri(uri)\n    http = Net::HTTP.new(uri.host, uri.port)\n    http.use_ssl = (uri.scheme == \"https\")","sourceCodeStart":269,"sourceCodeEnd":305,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/gems/canvas_http/lib/canvas_http.rb#L269-L305","documentation":"During blocklist validation, CanvasHttp converts each resolved address into an IPAddr. Addresses that fail to parse are logged as warnings and dropped; if nothing parseable remains, the host's IPs cannot be checked against the blocked ranges, so UnresolvableUriError is raised. This is a defensive guard against an 'impossible' Resolv behavior.","triggerScenarios":"CanvasHttp request helpers where Resolv.getaddresses returns entries that IPAddr.new cannot parse (corrupt resolver data, weird resolver output) so the filtered ip_addrs list ends up empty while blocked_ip_ranges is non-empty.","commonSituations":"Custom or broken DNS resolvers returning malformed strings; proxies/resolv.conf setups injecting unexpected values; running with an unusual Ruby/Resolv combination.","solutions":["Inspect logs for the 'CANVAS_HTTP WARNING | invalid_ip' lines to see what unparseable value is returned","Fix or replace the misbehaving DNS resolver configuration","Upgrade/verify the Ruby Resolv library behavior in the environment","As a workaround, rescue UnresolvableUriError and re-fetch after resolver fixes"],"exampleFix":"// before\nCanvasHttp.get('https://weird-host.example.com')\n// after\nbegin\n  CanvasHttp.get('https://weird-host.example.com')\nrescue CanvasHttp::UnresolvableUriError => e\n  Rails.logger.warn(\"host validation failed: #{e.message}\")\nend","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  CanvasHttp.get(url)\nrescue CanvasHttp::UnresolvableUriError => e\n  logger.warn(\"unparseable IPs for host: #{e.message}\")\n  nil\nend","preventionTips":["Check warning logs for invalid_ip values to spot broken resolvers","Keep Ruby/Resolv versions current","Use standard DNS resolvers in production environments"],"tags":["network","dns","ssrf-protection"],"backgroundTag":"dns-resolution-failed","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}