Freika/dawarich · warning · UrlValidatable::BlockedUrlError

Could not resolve hostname: %{host}

Error message

Could not resolve hostname: %{host}

What it means

Raised as BlockedUrlError when Resolv.getaddress raises Resolv::ResolvError during DNS lookup of the URL's host — the hostname cannot be resolved to any IP address. The validator resolves the host to run its SSRF range check, so an unresolvable name stops the flow with the offending host included in the message.

Source

Thrown at app/services/concerns/url_validatable.rb:82

      raise BlockedUrlError, I18n.t('services.concerns.url_validatable.invalid_scheme', scheme: uri.scheme)
    end
    raise BlockedUrlError, I18n.t('services.concerns.url_validatable.host_required') if uri.host.blank?

    # Cloud refuses URLs that embed credentials. Self-hosters legitimately
    # use http://user:pass@host — homelab Immich behind nginx basic-auth
    # is a real config we don't want to break.
    if uri.userinfo.present? && !DawarichSettings.self_hosted?
      raise BlockedUrlError, I18n.t('services.concerns.url_validatable.embedded_credentials')
    end

    ip = IPAddr.new(Resolv.getaddress(uri.host))
    if blocked_ranges.any? { |range| range.include?(ip) }
      raise BlockedUrlError, I18n.t('services.concerns.url_validatable.blocked_address')
    end
  rescue URI::InvalidURIError
    raise BlockedUrlError, I18n.t('services.concerns.url_validatable.invalid_format')
  rescue Resolv::ResolvError
    raise BlockedUrlError, I18n.t('services.concerns.url_validatable.unresolvable_host', host: uri.host)
  end

  def blocked_ranges
    if DawarichSettings.self_hosted?
      ALWAYS_BLOCKED_RANGES
    else
      ALWAYS_BLOCKED_RANGES + CLOUD_ONLY_BLOCKED_RANGES
    end
  end
end

View on GitHub (pinned to 97fad417c5)

Solutions

  1. From the server (or its container) run: getent hosts <host> / Resolv.getaddress('<host>') to confirm the name resolves in the app's network context, not just your laptop.
  2. Fix the hostname typo, or use an IP literal / publicly resolvable name.
  3. For Docker self-hosting, ensure the container uses a DNS server that can resolve your internal names (docker --dns, compose dns: block).
  4. If DNS is merely young, wait for propagation and retry the save.

Example fix

# before
url = 'http://immich.local:2283' # mDNS, Resolv raises -> BlockedUrlError

# after (container that can reach the host)
url = 'http://immich.lan.example.com:2283' # resolvable via LAN DNS configured in compose:
# services:
#   web:
#     dns: [192.168.1.1]
Defensive patterns

Strategy: validation

Validate before calling

Resolv.getaddress(URI.parse(url).host) # raises Resolv::ResolvError if unresolvable - pre-flight check

Type guard

def resolvable_url?(s)
  u = URI.parse(s.to_s)
  return false unless u.host
  Resolv.getaddress(u.host)
  true
rescue StandardError
  false
end

Try / catch

begin
  validate_integration_url!(url)
rescue BlockedUrlError => e
  errors.add(:url, "Hostname does not resolve from this server")
end

Prevention

When it happens

Trigger: Typos in the hostname ('immich.exmaple.com'), hosts that exist only on the user's LAN with no public DNS record, DNS outages or broken resolv.conf in the container running the app, freshly-created DNS records that have not propagated, or '.local' mDNS names which standard DNS resolvers cannot answer.

Common situations: Self-hosted Docker deployments where the app container lacks access to the LAN DNS that would resolve homelab names, users entering mDNS names like 'immich.local', DNS entries created seconds before, restrictive cloud egress DNS policies, IPv6-only hosts with broken A/AAAA resolution.

Understand the failure class

Related errors


AI-assisted analysis of Freika/dawarich@97fad417c5 (2026-08-21). Data as JSON: /api/errors/b49a47e3d62344df. Report an issue: GitHub.