grpc/grpc-go · error

delegating_resolver: unable to build the resolver for target

Error message

delegating_resolver: unable to build the resolver for target %s: %v

What it means

Returned by New() when targetResolverBuilder.Build(target, wcc, opts) fails while constructing the child target resolver (with a proxy active and target resolution enabled). The wrapped error comes from the underlying resolver builder (typically dns or a custom resolver registered via WithResolvers). The whole delegating resolver construction fails.

Source

Thrown at internal/resolver/delegatingresolver/delegatingresolver.go:158

	r.childMu.Lock()
	defer r.childMu.Unlock()
	// When the scheme is 'dns' and target resolution on client is not enabled,
	// resolution should be handled by the proxy, not the client. Therefore, we
	// bypass the target resolver and store the unresolved target address.
	if target.URL.Scheme == "dns" && !targetResolutionEnabled {
		r.targetResolverState = &resolver.State{
			Addresses: []resolver.Address{{Addr: addr}},
			Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: addr}}}},
		}
		r.updateTargetResolverState(*r.targetResolverState)
		return r, nil
	}
	wcc := &wrappingClientConn{
		stateListener: r.updateTargetResolverState,
		parent:        r,
	}
	if r.targetResolver, err = targetResolverBuilder.Build(target, wcc, opts); err != nil {
		return nil, fmt.Errorf("delegating_resolver: unable to build the resolver for target %s: %v", target, err)
	}
	return r, nil
}

// proxyURIResolver creates a resolver for resolving proxy URIs using the "dns"
// scheme. It adjusts the proxyURL to conform to the "dns:///" format and builds
// a resolver with a wrappingClientConn to capture resolved addresses.
func (r *delegatingResolver) proxyURIResolver(opts resolver.BuildOptions) (resolver.Resolver, error) {
	proxyBuilder := resolver.Get("dns")
	if proxyBuilder == nil {
		panic("delegating_resolver: resolver for proxy not found for scheme dns")
	}
	url := *r.proxyURL
	url.Scheme = "dns"
	url.Path = "/" + r.proxyURL.Host
	url.Host = "" // Clear the Host field to conform to the "dns:///" format

	proxyTarget := resolver.Target{URL: url}

View on GitHub (pinned to 03255a9237)

Solutions

  1. Inspect the wrapped error to see which builder failed and why.
  2. Ensure a resolver is registered for the target scheme (dns is default; register custom schemes via resolver.Register).
  3. For dns, verify the target endpoint is a valid host (optionally :port).
  4. Pass the right resolver mapping through grpc.WithResolvers for custom schemes.

Example fix

// before
resolver.Register(myBuilder) // registered after Dial
conn, _ := grpc.Dial("myscheme:///svc", ...)
// after
resolver.Register(myBuilder) // register BEFORE Dial
conn, _ := grpc.Dial("myscheme:///svc", grpc.WithResolvers(myBuilder))
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure a resolver exists for the scheme before dialing.
func schemeRegistered(s string) bool {
    return resolver.Get(s) != nil
}

Try / catch

conn, err := grpc.Dial(target, ...)
if err != nil && strings.Contains(err.Error(), "unable to build the resolver") {
    // Fall back to the default dns resolver form.
    conn, err = grpc.Dial("dns:///"+hostPort, ...)
}

Prevention

When it happens

Trigger: The dns resolver builder rejecting the target (bad host/port), a custom resolver builder returning an error from Build, or no resolver registered for the target's scheme.

Common situations: Dial target uses an unregistered scheme; custom resolver builder misconfigured via WithResolvers; dns authority portion of the URI unresolvable; version change that altered how schemes are resolved.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/477117fd0cb393e1. Report an issue: GitHub.