ory/hydra · error
DNS lookup timed out
Error message
DNS lookup timed out
What it means
ipx.IsAssociatedIPAllowed resolves hostnames before checking IP rules; it bounds DNS resolution with a 2-second context whose cause is errors.New("DNS lookup timed out"). When resolution exceeds that deadline (or fails with a timeout-class DNS error), the context's cause surfaces this message so callers can distinguish slow/unreachable DNS from disallowed destinations.
Source
Thrown at oryx/ipx/ip_validator.go:67
if parsed, err := url.ParseRequestURI(ipOrHostnameOrURL); err == nil {
ipOrHostname = parsed.Hostname()
}
if ip, err := netip.ParseAddr(ipOrHostname); err == nil {
if !allowed(ip) {
return errors.Errorf("ip %s is not a permitted destination", ip)
}
return nil
}
if addr, err := netip.ParseAddrPort(ipOrHostnameOrURL); err == nil {
if !allowed(addr.Addr()) {
return errors.Errorf("ip %s is not a permitted destination", addr.Addr())
}
return nil
}
ctx, cancel := context.WithTimeoutCause(ctx, 2*time.Second, errors.New("DNS lookup timed out"))
defer cancel()
ips, err := resolver.LookupNetIP(ctx, "ip", ipOrHostname)
if err != nil {
if dnsErr, ok := stderrors.AsType[*net.DNSError](err); ok {
// Copy the `*net.DNSError` before masking `Server` to avoid a data
// race: the DNS resolver uses `singleflight` to deduplicate
// concurrent lookups, so multiple goroutines may receive the same
// `*net.DNSError` pointer. Mutating it in place races with concurrent
// readers (e.g. the `otelhttp` `dnsDone` trace hook).
maskedDNS := *dnsErr
maskedDNS.Server = "" // Mask our DNS server's IP address.
return errors.Wrapf(&maskedDNS, "failed to resolve %s", ipOrHostnameOrURL)
}
}
for _, ip := range ips {
if !allowed(ip) {
return errors.Wrapf(&net.DNSError{View on GitHub (pinned to 4174065ffb)
Solutions
- Retry the operation — the failure may be transient resolver latency; the 2s bound is fixed, so retrying is the primary mitigation.
- Resolve hostnames with your own resolver (with a larger deadline) and pass the resolved IP instead of the hostname, skipping the lookup path.
- Ensure DNS infrastructure is healthy: correct /etc/resolv.conf, reachable upstream resolvers, sane ndots/search config in containers.
- Allowlist known internal hostnames as IPs ahead of time so the lookup path is not exercised at request time.
Example fix
// before allowed, err := ipx.IsAssociatedIPAllowedWhenSet(ctx, "internal.svc.cluster.local", set) // after ips, err := myResolver.LookupHost(ctx2, "internal.svc.cluster.local") // own, longer deadline allowed, err = ipx.IsAssociatedIPAllowedWhenSet(ctx, ips[0], set)
Defensive patterns
Strategy: retry
Validate before calling
// pre-resolve with your own deadline if DNS is slow:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ips, err := net.DefaultResolver.LookupNetIP(ctx, "ip", hostname)
if err != nil { return fmt.Errorf("unresolvable host %q: %w", hostname, err) }
// then call IsAssociatedIPAllowed with ips[0].String() Type guard
func isDNSTimeout(err error) bool {
var dnsErr *net.DNSError
return errors.As(err, &dnsErr) && dnsErr.IsTimeout
} Try / catch
allowed, err := ipx.IsAssociatedIPAllowedWhenSet(ctx, host, set)
if err != nil {
if strings.Contains(err.Error(), "DNS lookup timed out") || isDNSTimeout(err) {
return retry.WithBackoff(func() error {
var e error
allowed, e = ipx.IsAssociatedIPAllowedWhenSet(ctx, host, set)
return e
})
}
return err
} Prevention
- Retry once or twice — the 2s bound makes transient slowness common
- Fix resolver config (resolv.conf, ndots, search domains) in containers
- Pass pre-resolved IPs for known internal hostnames
- Monitor resolver latency; alert when p99 nears 2s
When it happens
Trigger: Calling IsAssociatedIPAllowed (directly or via IsAssociatedIPAllowedWhenSet, or SSRF-check helpers) with ipOrHostname set to a hostname whose DNS resolution does not complete within 2 seconds — e.g. an internal name only resolvable by a slow corporate resolver, or a resolver outage.
Common situations: SSRF-checking a webhook URL whose host is an internal .local/cluster-internal name that public resolvers cannot answer; DNS infrastructure degradation (resolver timeout, ndots search-domain churn in k8s); IPv6-only hostnames with broken AAAA paths.
Understand the failure class
- DNS resolution errors: ENOTFOUND and getaddrinfo failures — how hostname lookups fail and how to debug them.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- key %s validation is failing
- no route to host
- ip %s is not a permitted destination
- could not start debug server on port %d: %w
- newWorker: warm up failed
AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03).
Data as JSON: /api/errors/6aed7b7be9b22ef2.
Report an issue: GitHub.