postalserver/postal · error · Postal::HTTP::BlockedDestinationError

Could not resolve '#{@host}' to any IP address

Error message

Could not resolve '#{@host}' to any IP address

What it means

AddressGuard resolves the host of every outbound request (via Resolv) and refuses to continue when zero A/AAAA records come back. The error string interpolates the offending host, so the message reads e.g. Could not resolve 'hooks.exampl.com' to any IP address. It is raised as BlockedDestinationError, which Postal::HTTP maps to a failed request rather than connecting.

Source

Thrown at lib/postal/http/address_guard.rb:102

          end
          families.uniq
        end

      end

      # @param [String] host
      def initialize(host)
        @host = host.to_s
      end

      def safe_connect_address
        if @host.empty?
          raise BlockedDestinationError, "No host was given for the request"
        end

        addresses = resolve
        if addresses.empty?
          raise BlockedDestinationError, "Could not resolve '#{@host}' to any IP address"
        end

        # Reject the whole request if *any* resolved address is blocked. This is
        # checked before the reachability filtering below so that a blocked
        # destination is always reported as such, regardless of which address
        # families this particular server can reach. It also defeats DNS
        # responses that mix a public and a private address to slip past.
        addresses.each do |address|
          next unless blocked?(address)

          raise BlockedDestinationError,
                "Destination '#{@host}' (#{address}) is not permitted"
        end

        # Only connect to an address whose family this server can actually
        # reach. Otherwise we might pin the connection to an IPv6 address on a
        # host without IPv6 connectivity and fail to connect even when a usable
        # IPv4 address was available.

View on GitHub (pinned to d038eaa8c7)

Solutions

  1. Verify the hostname resolves from the Postal host: dig +short <host> or getent hosts <host>
  2. Fix the typo/expired domain in the endpoint URL
  3. If the record is new, wait for propagation and retry delivery
  4. If DNS works elsewhere but not from Postal, fix the host's resolver (/etc/resolv.conf, docker --dns, cluster DNS)

Example fix

# before
endpoint.url = "https://hooks.exampl.com/receive"

# after
endpoint.url = "https://hooks.example.com/receive"

# sanity check from the Postal host:
# dig +short hooks.example.com  # must return at least one A/AAAA record
Defensive patterns

Strategy: validation

Validate before calling

# pre-flight DNS check before configuring or calling the endpoint
require "resolv"
addresses = Resolv::DNS.open { |dns| dns.getresources(host, Resolv::DNS::Resource::IN::A) + dns.getresources(host, Resolv::DNS::Resource::IN::AAAA) }
raise ArgumentError, "#{host} does not resolve" if addresses.empty?

Type guard

def resolvable_host?(host)
  require "resolv"
  Resolv::DNS.open do |dns|
    dns.getresources(host, Resolv::DNS::Resource::IN::A).any? ||
      dns.getresources(host, Resolv::DNS::Resource::IN::AAAA).any?
  end
end

Try / catch

begin
  Postal::HTTP.request(...)
rescue Postal::HTTP::BlockedDestinationError => e
  if e.message.include?("Could not resolve")
    # DNS problem: surface it (typo/propagation) rather than retrying immediately
  end
end

Prevention

When it happens

Trigger: An HTTPEndpoint/webhook URL whose hostname has no DNS records: typo'd domain (double letters, .con instead of .com), an expired/dropped domain, a freshly created DNS record not yet propagated, or the Postal host's resolver being broken (empty/misconfigured /etc/resolv.conf, unreachable DNS in the container).

Common situations: Typo in the webhook hostname; recently registered domain whose records have not propagated; internal-only DNS names that the Postal host's resolver does not know; containerized Postal with missing DNS config; temporary resolver outage at delivery time.

Related errors


AI-assisted analysis of postalserver/postal@d038eaa8c7 (2026-08-21). Data as JSON: /api/errors/08ee803300df8cdf. Report an issue: GitHub.