instructure/canvas-lms · error · UnresolvableUriError
# resolves to only unparseable IPs...
Error message
#{host} resolves to only unparseable IPs... What it means
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.
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
Example fix
// before
CanvasHttp.get('https://weird-host.example.com')
// after
begin
CanvasHttp.get('https://weird-host.example.com')
rescue CanvasHttp::UnresolvableUriError => e
Rails.logger.warn("host validation failed: #{e.message}")
end Defensive patterns
Strategy: try-catch
Try / catch
begin
CanvasHttp.get(url)
rescue CanvasHttp::UnresolvableUriError => e
logger.warn("unparseable IPs for host: #{e.message}")
nil
end Prevention
- Check warning logs for invalid_ip values to spot broken resolvers
- Keep Ruby/Resolv versions current
- Use standard DNS resolvers in production environments
When it happens
Trigger: 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.
Common situations: Custom or broken DNS resolvers returning malformed strings; proxies/resolv.conf setups injecting unexpected values; running with an unusual Ruby/Resolv combination.
Related errors
- # cannot be resolved to any address
- CANVAS_HTTP WARNING | host: #
- Consul is unavailable because the circuit breaker has…
- could not retrieve configuration, the server response timed…
- Couldn't send LTI AGS grade progress metric
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/0d8e9456db4efe58.
Report an issue: GitHub.
Appendix: source
Thrown at gems/canvas_http/lib/canvas_http.rb:287
return false if blocked_ip_ranges.empty?
resolved_addrs = Resolv.getaddresses(host)
unless resolved_addrs.any?
# this is actually a different condition than the host being insecure,
# and having separate telemetry is helpful for understanding transient failures.
raise UnresolvableUriError, "#{host} cannot be resolved to any address"
end
ip_addrs = resolved_addrs.filter_map do |ip|
::IPAddr.new(ip)
rescue IPAddr::InvalidAddressError
# this should never happen, Resolv should only be passing back IPs, but
# let's make sure we can see if the impossible occurs
logger.warn("CANVAS_HTTP WARNING | host: #{host} | invalid_ip: #{ip}")
nil
end
unless ip_addrs.any?
raise UnresolvableUriError, "#{host} resolves to only unparseable IPs..."
end
blocked_ip_ranges.each do |range|
addr_range = ::IPAddr.new(range)
ip_addrs.any? do |addr|
if addr_range.include?(addr)
logger.warn("CANVAS_HTTP WARNING insecure address | host: #{host} | insecure_address: #{addr} | range: #{range}")
return true
end
end
end
false
end
# returns a Net::HTTP connection object for the given URI object
def self.connection_for_uri(uri)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = (uri.scheme == "https")View on GitHub (pinned to 1c9f0bb801)