grpc/grpc-go · critical

failed to create child policy of type

Error message

failed to create child policy of type %s: %v

What it means

Returned by updateChildConfig (line 273) when newChildBalancer(b.cc, b.bOpts) fails. newChildBalancer (lines 46-54) calls balancer.Get(priority.Name) and returns error 341 if nil. So this error wraps error 341 with additional context naming the child policy type. This fires on the first config update when b.childLB is nil and the child priority balancer cannot be constructed.

Solutions

  1. Follow the remediation for error 341: import the full xds package or ensure _ "google.golang.org/grpc/internal/xds/balancer/priority" is imported.
  2. Check go.mod for version consistency across grpc-go modules.
  3. Verify no init-order issue is preventing the priority package's init() from running before the CDS balancer is used.

Example fix

// before: missing priority balancer registration
import (
    _ "google.golang.org/grpc/internal/xds/balancer/cdsbalancer"
)

// after: public xds package registers the full balancer chain
import (
    _ "google.golang.org/grpc/xds"
)
Defensive patterns

Strategy: validation

Validate before calling

// Startup validation: ensure priority balancer is registered
func init() {
    if balancer.Get("priority_experimental") == nil {
        log.Fatal("priority balancer not registered — import _ \"google.golang.org/grpc/xds\"")
    }
}

Try / catch

// Error from UpdateClientConnState propagates to TRANSIENT_FAILURE.
if strings.Contains(err.Error(), "failed to create child policy") {
    // root cause: unregistered priority balancer; fix imports
}

Prevention

When it happens

Trigger: The first time updateChildConfig runs for a CDS balancer (childLB is nil at line 270), it tries to create the priority child balancer. If the priority builder is not registered, newChildBalancer returns error 341 which is wrapped here as 'failed to create child policy of type priority: ...'.

Common situations: Identical to error 341: the priority balancer package is not imported/registered. This is the user-facing wrapper of that root cause, seen during a cluster config update rather than at Build() time.

Related errors


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

Appendix: source

Thrown at internal/xds/balancer/cdsbalancer/cdsbalancer.go:273

	if err := json.Unmarshal(clusterConfig.Cluster.LBPolicy, &b.xdsLBPolicy); err != nil {
		return b.annotateErrorWithNodeID(fmt.Errorf("error unmarshalling xDS LB Policy: %v", err))
	}
	if err := b.updateChildConfig(); err != nil {
		return b.annotateErrorWithNodeID(err)
	}
	return nil
}

// updateChildConfig builds child policy configuration using endpoint addresses
// returned from the XDSConfig and child policy configuration.
//
// A child policy is created if one doesn't already exist. The newly built
// configuration is then pushed to the child policy.
func (b *cdsBalancer) updateChildConfig() error {
	if b.childLB == nil {
		childLB, err := newChildBalancer(b.cc, b.bOpts)
		if err != nil {
			return fmt.Errorf("failed to create child policy of type %s: %v", priority.Name, err)
		}
		b.childLB = childLB
	}

	childCfgBytes, endpoints, err := buildPriorityConfigJSON(b.priorities, &b.xdsLBPolicy)
	if err != nil {
		return fmt.Errorf("failed to build child policy config: %v", err)
	}
	childCfg, err := b.childConfigParser.ParseConfig(childCfgBytes)
	if err != nil {
		return fmt.Errorf("failed to parse child policy config. This should never happen because the config was generated: %v", err)
	}
	if b.logger.V(2) {
		b.logger.Infof("Built child policy config: %s", pretty.ToJSON(childCfg))
	}

	for i := range endpoints {
		for j := range endpoints[i].Addresses {

View on GitHub (pinned to 0c51461d27)