vitessio/vitess · error

failed to initialize service config with load balancer polic

Error message

failed to initialize service config with load balancer policy %s: %s

What it means

During resolver build, if a BalancerPolicy is configured, the builder constructs a gRPC service config JSON ({"loadBalancingConfig": [{policy: {}}]}) and asks the ClientConn to parse it. If ParseServiceConfig fails (unknown/unregistered LB policy name or malformed config), this error wraps the parse error with the policy that caused it.

Source

Thrown at go/vt/vtadmin/cluster/resolver/resolver.go:265

}

func (b *builder) build(target grpcresolver.Target, cc grpcresolver.ClientConn, opts grpcresolver.BuildOptions) (*resolver, error) {
	var fn func(context.Context, []string) ([]string, error)
	switch target.URL.Host {
	case "vtctld":
		fn = b.opts.Discovery.DiscoverVtctldAddrs
	case "vtgate":
		fn = b.opts.Discovery.DiscoverVTGateAddrs
	default:
		return nil, fmt.Errorf("%s: unsupported URL host %s", logPrefix, target.URL.Host)
	}

	var sc serviceconfig.Config
	if b.opts.BalancerPolicy != "" {
		// c.f. https://github.com/grpc/grpc/blob/master/doc/service_config.md#example
		scpr := cc.ParseServiceConfig(fmt.Sprintf(`{"loadBalancingConfig": [{ "%s": {} }] }`, b.opts.BalancerPolicy))
		if scpr.Err != nil {
			return nil, fmt.Errorf("failed to initialize service config with load balancer policy %s: %s", b.opts.BalancerPolicy, scpr.Err)
		}

		sc = scpr.Config
	}

	ctx, cancel := context.WithCancel(context.Background())

	r := &resolver{
		component: target.URL.Host,
		// use the original cluster ID (not the sanitized scheme) for debugging/logging
		cluster:         b.clusterID,
		discoverAddrs:   fn,
		backoffStrategy: backoff.Get(b.opts.BackoffStrategy, b.opts.BackoffConfig),
		opts:            b.opts,
		cc:              cc,
		sc:              sc,
		rn:              make(chan struct{}, 1),
		ctx:             ctx,

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Confirm the policy string matches the gRPC-registered balancer name ('pickfirst', 'round_robin').
  2. Cross-check with BalancerPolicy.Set validation — use a value accepted there.
  3. Read the wrapped %s (scpr.Err) for gRPC's own explanation.
  4. Remove BalancerPolicy so the default service config is used.

Example fix

// before
opts.BalancerPolicy = "pick_first"
// after
opts.BalancerPolicy = "pickfirst"
Defensive patterns

Strategy: validation

Validate before calling

if policy != "" {
  sc := fmt.Sprintf(`{"loadBalancingConfig": [{ %q: {} }]}`, policy)
  if _, err := json.Marshal(json.RawMessage(sc)); err != nil {
    return fmt.Errorf("invalid service config: %w", err)
  }
  bp := resolver.BalancerPolicy("")
  if err := bp.Set(policy); err != nil {
    return fmt.Errorf("policy %q will fail service config parse", policy)
  }
}

Type guard

func isRegisteredGRPCLBPolicy(s string) bool {
  return balancer.Get(s) != nil
}

Try / catch

cc, err := resolver.Build(target, opts)
if err != nil {
  if strings.Contains(err.Error(), "failed to initialize service config") {
    opts.BalancerPolicy = ""
    cc, err = resolver.Build(target, opts)
  }
  if err != nil { return err }
}

Prevention

When it happens

Trigger: Setting opts.BalancerPolicy to a string that is not a registered gRPC load-balancing policy in this binary (e.g. custom policy never registered via balancer.Register), then calling Build/mustBuild.

Common situations: Mismatch between the allowed BalancerPolicy flag values and gRPC's registered balancer names (e.g. registering 'pick_first' in flag but gRPC expects 'pickfirst'); stripped builds where default balancers are not linked in.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/848e2d2add5aad0c. Report an issue: GitHub.