ory/hydra · error

key %s validation is failing

Error message

key %s validation is failing

What it means

AreAllAssociatedIPsAllowed validates a map of key -> IP/hostname/URL pairs concurrently with an errgroup. If any single pair fails IsAssociatedIPAllowed (e.g. the address is a non-permitted destination or DNS resolution fails), that error is wrapped as 'key <key> validation is failing' and returned, identifying which entry in the map was rejected.

Source

Thrown at oryx/ipx/ip_validator.go:34

	"github.com/pkg/errors"
)

// IsAssociatedIPAllowedWhenSet is a wrapper for IsAssociatedIPAllowed which returns valid
// when ipOrHostnameOrURL is empty.
func IsAssociatedIPAllowedWhenSet(ctx context.Context, ipOrHostnameOrURL string) error {
	if ipOrHostnameOrURL == "" {
		return nil
	}
	return IsAssociatedIPAllowed(ctx, ipOrHostnameOrURL)
}

// AreAllAssociatedIPsAllowed fails if one of the pairs is failing.
func AreAllAssociatedIPsAllowed(ctx context.Context, pairs map[string]string) error {
	g, ctx := errgroup.WithContext(ctx)
	for key, ipOrHostnameOrURL := range pairs {
		g.Go(func() error {
			return errors.Wrapf(IsAssociatedIPAllowed(ctx, ipOrHostnameOrURL), "key %s validation is failing", key)
		})
	}
	return g.Wait()
}

// IsAssociatedIPAllowed returns nil for a domain (with NS lookup), IP, or IPv6 address if it
// does not resolve to a private IP subnet. This is a first level of defense against
// SSRF attacks by disallowing any domain or IP to resolve to a private network range.
//
// Please keep in mind that validations for domains is valid only when looking up.
// A malicious actor could easily update the DSN record post validation to point
// to an internal IP
func IsAssociatedIPAllowed(ctx context.Context, ipOrHostnameOrURL string) error {
	ipOrHostname := ipOrHostnameOrURL
	if parsed, err := url.ParseRequestURI(ipOrHostnameOrURL); err == nil {
		ipOrHostname = parsed.Hostname()
	}

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Look at the wrapped inner error to see which check failed for that key
  2. Replace the offending value for that key with a public, resolvable IP/hostname
  3. If the endpoint is legitimately internal (dev environment), relax/adjust the IP allowlist configuration
  4. Verify DNS resolution for the hostname (dig/nslookup) if the failure is lookup-related

Example fix

// before
pairs := map[string]string{"webhook": "http://127.0.0.1:9090/callback"}
err := ipx.AreAllAssociatedIPsAllowed(ctx, pairs) // fails: loopback not permitted
// after
pairs := map[string]string{"webhook": "https://api.example.com/callback"}
err := ipx.AreAllAssociatedIPsAllowed(ctx, pairs)
Defensive patterns

Strategy: validation

Validate before calling

for key, addr := range pairs {
    if net.ParseIP(addr) == nil {
        if _, err := net.LookupHost(addr); err != nil {
            return fmt.Errorf("key %s: cannot resolve %q", key, addr)
        }
    }
}

Try / catch

if err := ipx.AreAllAssociatedIPsAllowed(ctx, pairs); err != nil {
    var keyErr *fmt.WrapError
    if errors.As(err, &keyErr) {
        log.Printf("failed pair identified: %v", err) // message names the key
    }
}

Prevention

When it happens

Trigger: Calling AreAllAssociatedIPsAllowed with a map where at least one value is an IP/hostname/URL that fails IsAssociatedIPAllowed — e.g. points to a private/loopback address when such destinations are forbidden, or a hostname whose NS lookup fails.

Common situations: Validating claimed redirect/allowed endpoints (e.g. courier SMTP or webhook configuration) where one configured host resolves to localhost or an internal IP; typo'd hostnames that fail DNS; SSRF-protection rejecting internal endpoints in dev/staging configs.

Related errors


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/eb316d3718e628a9. Report an issue: GitHub.