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

After the proxy URL is resolved (or found unnecessary), the delegating resolver constructs the actual target resolver by calling targetResolverBuilder.Build(target, wcc, opts) at delegatingresolver.go:157. If the underlying scheme-specific resolver (dns, passthrough, unix, etc.) fails to build, New() wraps and returns the error.

Solutions

  1. Read the wrapped %v to identify which child resolver failed and why.
  2. Validate the dial target string conforms to the scheme's expectations (e.g. "unix:///path/to/socket", "dns:///host:port").
  3. If using a custom resolver via grpc.WithResolvers, ensure its Build does not error for the given target.
  4. Test the target with the scheme's resolver directly to isolate the failure.

Example fix

// before:
//   conn, err := grpc.Dial("unix://localhost/tmp/x.sock", opts)
//   // unix resolver rejects non-empty authority "localhost"

// after:
//   conn, err := grpc.Dial("unix:///tmp/x.sock", opts)
Defensive patterns

Strategy: try-catch

Validate before calling

// If you register a custom resolver, smoke-test its Build before relying on
// it in production.
//
//   b := resolver.Get(scheme)
//   if b == nil { log.Fatalf("no resolver registered for scheme %q", scheme) }
//   r, err := b.Build(target, cc, opts)
//   if err != nil { log.Fatalf("resolver build failed: %v", err) }
//   r.Close()

Try / catch

// Dial surfaces the wrapped build error synchronously.
//
//   conn, err := grpc.Dial(target, opts)
//   if err != nil {
//       return fmt.Errorf("dial %s failed: %w", target, err)
//   }

Prevention

When it happens

Trigger: Triggered when targetResolverBuilder.Build returns a non-nil error during delegatingresolver.New while a proxy is configured (so the delegating resolver is in use) and target resolution is enabled.

Common situations: The scheme-specific resolver rejects the target (e.g. unix resolver sees a non-empty authority), a custom resolver registered via grpc.WithResolvers fails, or the target URI is malformed for the chosen scheme.

Related errors


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

Appendix: 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 0c51461d27)