grpc/grpc-go · error

weighted-target: no targets to pick from

Error message

weighted-target: no targets to pick from

What it means

Internal error from the weighted-target aggregator (balancer/weightedtarget/weightedaggregator/aggregator.go:255-260) returned when idToPickerState is empty — i.e. all sub-balancers (localities/targets) have been removed. The aggregator emits a TransientFailure state with a NewErrPicker wrapping this message.

Source

Thrown at balancer/weightedtarget/weightedaggregator/aggregator.go:259

		// need to call it when they are resumed.
		wbsa.needUpdateStateOnResume = true
		return
	}

	wbsa.cc.UpdateState(wbsa.build())
}

// build combines sub-states into one.
//
// Caller must hold wbsa.mu.
func (wbsa *Aggregator) build() balancer.State {
	wbsa.logger.Infof("Child pickers with config: %+v", wbsa.idToPickerState)

	if len(wbsa.idToPickerState) == 0 {
		// This is the case when all sub-balancers are removed.
		return balancer.State{
			ConnectivityState: connectivity.TransientFailure,
			Picker:            base.NewErrPicker(errors.New("weighted-target: no targets to pick from")),
		}
	}

	// Make sure picker's return error is consistent with the aggregatedState.
	pickers := make([]weightedPickerState, 0, len(wbsa.idToPickerState))

	switch aggState := wbsa.csEvltr.CurrentState(); aggState {
	case connectivity.Connecting:
		return balancer.State{
			ConnectivityState: aggState,
			Picker:            base.NewErrPicker(balancer.ErrNoSubConnAvailable)}
	case connectivity.TransientFailure:
		// this means that all sub-balancers are now in TransientFailure.
		for _, ps := range wbsa.idToPickerState {
			pickers = append(pickers, *ps)
		}
		return balancer.State{
			ConnectivityState: aggState,

View on GitHub (pinned to 03255a9237)

Solutions

  1. Inspect the xDS / weighted-target config in the control plane: the targets map must contain at least one locality with weight > 0.
  2. If sub-balancers are intentionally removable, expect TransientFailure and either fail over to another channel or wait for the next config update (use grpc.WaitForReady(true)).
  3. Verify the parent (cluster_resolver / xds) is forwarding the right weighted-target config and not an empty one during reconciliation.
  4. Check channelz for the weighted_target balancer children to confirm which localities were present before removal.

Example fix

// before — weighted_target config with no targets
{
  "targets": {},            // -> 'weighted-target: no targets to pick from'
  "targetsFieldCase": "targets"
}

// after — provide at least one locality
{
  "targets": {
    "loc1": { "weight": 1, "childPolicy": [{"round_robin": {}}] }
  }
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate weighted-target config has at least one target
func validateWT(cfg *weightedtargetConfig) error {
    if len(cfg.Targets) == 0 {
        return errors.New("weighted-target: no targets configured")
    }
    return nil
}

Try / catch

if strings.Contains(err.Error(), "weighted-target: no targets to pick from") {
    // control plane delivered an empty locality set; fail over or wait
}

Prevention

When it happens

Trigger: Aggregator.build() runs with len(wbsa.idToPickerState)==0. This is the case 'when all sub-balancers are removed' per the inline comment. Concretely: an xDS weighted_target/clusters update dropped every locality, or a config update cleared all weighted targets while the channel is still up.

Common situations: xDS management server sent a ClusterLoadAssignment / ListenersConfiguration with zero localities for the cluster; weighted-target config object has an empty targets map; transient control-plane state during a rolling update removing all endpoints; misconfigured traffic splitting with no fallback locality.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/622c0f09bffa3390. Report an issue: GitHub.