grpc/grpc-go · error
could not get resolver for default scheme: %q
Error message
could not get resolver for default scheme: %q
What it means
In initParsedTargetAndResolverBuilder (clientconn.go:1852-1854), when the dial target had no scheme or an unregistered scheme, gRPC falls back to the default scheme and re-parses. If getResolver() still returns nil for that fallback scheme, the channel cannot proceed. The %q is the fallback scheme string that has no registered builder.
Source
Thrown at clientconn.go:1854
// We are here because the user's dial target did not contain a scheme or
// specified an unregistered scheme. We should fallback to the default
// scheme, except when a custom dialer is specified in which case, we should
// always use passthrough scheme. For either case, we need to respect any overridden
// global defaults set by the user.
defScheme := cc.dopts.defaultScheme
if internal.UserSetDefaultScheme {
defScheme = resolver.GetDefaultScheme()
}
canonicalTarget := defScheme + ":///" + cc.target
parsedTarget, err = parseTarget(canonicalTarget)
if err != nil {
return err
}
rb = cc.getResolver(parsedTarget.URL.Scheme)
if rb == nil {
return fmt.Errorf("could not get resolver for default scheme: %q", parsedTarget.URL.Scheme)
}
cc.parsedTarget = parsedTarget
cc.resolverBuilder = rb
return nil
}
// parseTarget uses RFC 3986 semantics to parse the given target into a
// resolver.Target struct containing url. Query params are stripped from the
// endpoint.
func parseTarget(target string) (resolver.Target, error) {
u, err := url.Parse(target)
if err != nil {
return resolver.Target{}, err
}
return resolver.Target{URL: *u}, nil
}
View on GitHub (pinned to 03255a9237)
Solutions
- Register a resolver builder for the scheme shown in the error (resolver.Register), or import the package that registers it.
- Prefix the target with a registered scheme such as dns:/// or passthrough:///.
- If you set a custom default scheme, ensure its builder is registered before Dial.
Example fix
// before
conn, err := grpc.NewClient("myservice:443")
// after
import _ "google.golang.org/grpc/internal/resolver/dns"
conn, err := grpc.NewClient("dns:///myservice:443") Defensive patterns
Strategy: validation
Validate before calling
// Confirm a builder is registered for the effective scheme.
func checkScheme(scheme string) error {
for _, s := range resolver.GetSchemes() { if s == scheme { return nil } }
return fmt.Errorf("unregistered scheme: %q", scheme)
} Prevention
- Always prefix dial targets with an explicit, registered scheme (dns:///, passthrough:///).
- If you override the default scheme, register its builder first.
- Fail fast at startup with a scheme check rather than at dial time.
When it happens
Trigger: Dialing with a bare target (no scheme) when the default scheme's resolver isn't registered, or explicitly using a scheme with no builder. Common after switching default scheme via UserSetDefaultScheme to something unregistered.
Common situations: Forgetting to register a custom resolver; setting a custom default scheme that has no builder; running a minimal binary that didn't import the dns resolver.
Related errors
- failed to exit idle mode: %w
- failed to start resolver: %w
- dns resolver: missing address
- dns resolver: missing port after port-separator colon
- passthrough: received empty target in Build()
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/72c91b26ace740bf.
Report an issue: GitHub.