grpc/grpc-go · critical

grpc: the provided default service config is invalid: %v

Error message

grpc: the provided default service config is invalid: %v

What it means

WithDefaultServiceConfig(s) stores a raw JSON service config applied as fallback when the resolver provides none (or when WithDisableServiceConfig is set). During NewClient, clientconn.go:229-233 parses it via parseServiceConfig; if parsing fails, NewClient returns nil and this error, because a broken default config would leave the channel unable to pick a balancer.

Source

Thrown at clientconn.go:232

	if err := cc.initParsedTargetAndResolverBuilder(); err != nil {
		return nil, err
	}

	for _, opt := range globalPerTargetDialOptions {
		opt.DialOptionForTarget(cc.parsedTarget.URL).apply(&cc.dopts)
	}

	chainUnaryClientInterceptors(cc)
	chainStreamClientInterceptors(cc)

	if err := cc.validateTransportCredentials(); err != nil {
		return nil, err
	}

	if cc.dopts.defaultServiceConfigRawJSON != nil {
		scpr := parseServiceConfig(*cc.dopts.defaultServiceConfigRawJSON, cc.dopts.maxCallAttempts)
		if scpr.Err != nil {
			return nil, fmt.Errorf("%s: %v", invalidDefaultServiceConfigErrPrefix, scpr.Err)
		}
		cc.dopts.defaultServiceConfig, _ = scpr.Config.(*ServiceConfig)
	}
	cc.keepaliveParams = cc.dopts.copts.KeepaliveParams

	if err = cc.initAuthority(); err != nil {
		return nil, err
	}

	// Register ClientConn with channelz. Note that this is only done after
	// channel creation cannot fail.
	cc.channelzRegistration(target)
	channelz.Infof(logger, cc.channelz, "parsed dial target is: %#v", cc.parsedTarget)
	channelz.Infof(logger, cc.channelz, "Channel authority set to %q", cc.authority)

	cc.csMgr = newConnectivityStateManager(cc.ctx, cc.channelz)
	cc.pickerWrapper = newPickerWrapper()

View on GitHub (pinned to 0c51461d27)

Solutions

  1. Run the JSON through a validator and against the gRPC service config schema (https://github.com/grpc/grpc/blob/master/doc/service_config.md).
  2. Ensure any balancer referenced in loadBalancingConfig is registered (import its package side-effect, e.g., _ "google.golang.org/grpc/balancer/weightedroundrobin").
  3. Simplify to a known-good config first (e.g., {"loadBalancingConfig":[{"pick_first":{}}]}) then add fields back incrementally.

Example fix

// before
cc, err := grpc.NewClient(target, grpc.WithDefaultServiceConfig(
  `{"loadBalancingConfig":[{"round_robin"}]}`, // missing colon/braces
)
// after
cc, err := grpc.NewClient(target, grpc.WithDefaultServiceConfig(
  `{"loadBalancingConfig":[{"round_robin":{}}]}`,
))
Defensive patterns

Strategy: validation

Validate before calling

func validateDefaultServiceConfigJSON(js string) error {
	pr := parseServiceConfig(js, 1) // maxAttempts; or use serviceconfig.Parse via the balancer registry
	return pr.Err
}

Try / catch

// always check the error from NewClient
cc, err := grpc.NewClient(target, grpc.WithDefaultServiceConfig(sc))
if err != nil {
    return fmt.Errorf("invalid default service config: %w", err)
}

Prevention

When it happens

Trigger: Calling grpc.NewClient(target, grpc.WithDefaultServiceConfig(badJSON)) where badJSON is invalid JSON, references an unregistered balancer, or has malformed loadBalancingConfig/retryPolicy fields. parseServiceConfig returns a non-nil scpr.Err and clientconn.go:232 wraps it.

Common situations: A typo in the service config JSON string; referencing a balancer name that is not registered (e.g., custom balancer not imported); malformed retry policy; quoting errors when building the string programmatically.

Related errors


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