grpc/grpc-go · error

invalid loadBalancingConfig: no supported policies found in

Error message

invalid loadBalancingConfig: no supported policies found in %v

What it means

Fires at serviceconfig.go:125 after BalancerConfig.UnmarshalJSON walks every loadBalancingConfig entry and finds none whose policy name maps to a registered balancer. gRPC iterates entries in order and uses the first supported policy; if the list contains only unknown policy names, the whole service config is rejected as invalid rather than silently falling back.

Solutions

  1. Cross-check each policy name in the error's %v list against balancer.Get registrations; blank-import the missing package (e.g. _ "google.golang.org/grpc/balancer/roundrobin").
  2. Verify the spelling of policy names; note built-in names like "pick_first" and "round_robin" use snake_case.
  3. If a policy is genuinely unavailable on this client version, either upgrade the client or have the config producer prepend a policy the client does support.
  4. For xDS-managed clients, ensure the xds resolver and its balancers are imported and the client version matches the config source.

Example fix

// before: service config only references grpclb, which is not imported
// "loadBalancingConfig": [{"grpclb": {}}]

// after: import the balancer and/or reference a supported policy
import _ "google.golang.org/grpc/balancer/grpclb"
// or
// "loadBalancingConfig": [{"round_robin": {}}, {"grpclb": {}}]
Defensive patterns

Strategy: validation

Validate before calling

import (
    "google.golang.org/grpc/balancer"
    _ "google.golang.org/grpc/balancer/roundrobin"
    _ "google.golang.org/grpc/balancer/grpclb"
)

func ensureBalancersRegistered(names []string) error {
    for _, n := range names {
        if balancer.Get(n) == nil {
            return fmt.Errorf("balancer %q is not registered; import its package", n)
        }
    }
    return nil
}

Try / catch

var bc svcconfig.BalancerConfig
if err := json.Unmarshal(raw, &bc); err != nil {
    if strings.Contains(err.Error(), "no supported policies found") {
        // import the referenced balancer packages, or prepend a supported policy
    }
    return err
}

Prevention

When it happens

Trigger: A service config whose loadBalancingConfig array lists only balancer names that are not registered in the binary. Examples: [{"grpclb": {}}] when the grpclb balancer was never imported or was removed; [{"rls": {}}] without importing the RLS balancer; policy names from a newer gRPC than the client.

Common situations: Using a service config designed for a different gRPC feature set (xDS/RLS/cds) on a plain client; a balancer package removed or never blank-imported; version skew between the control plane emitting config and the client consuming it; using "round_robin" spelling when the registered name differs.

Related errors


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

Appendix: source

Thrown at internal/serviceconfig/serviceconfig.go:125

			if string(jsonCfg) != "{}" {
				logger.Warningf("non-empty balancer configuration %q, but balancer does not implement ParseConfig", string(jsonCfg))
			}
			// Stop at this, though the builder doesn't support parsing config.
			return nil
		}

		cfg, err := parser.ParseConfig(jsonCfg)
		if err != nil {
			return fmt.Errorf("error parsing loadBalancingConfig for policy %q: %v", name, err)
		}
		bc.Config = cfg
		return nil
	}
	// This is reached when the for loop iterates over all entries, but didn't
	// return. This means we had a loadBalancingConfig slice but did not
	// encounter a registered policy. The config is considered invalid in this
	// case.
	return fmt.Errorf("invalid loadBalancingConfig: no supported policies found in %v", names)
}

// MethodConfig defines the configuration recommended by the service providers for a
// particular method.
type MethodConfig struct {
	// WaitForReady indicates whether RPCs sent to this method should wait until
	// the connection is ready by default (!failfast). The value specified via the
	// gRPC client API will override the value set here.
	WaitForReady *bool
	// Timeout is the default timeout for RPCs sent to this method. The actual
	// deadline used will be the minimum of the value specified here and the value
	// set by the application via the gRPC client API.  If either one is not set,
	// then the other will be used.  If neither is set, then the RPC has no deadline.
	Timeout *time.Duration
	// MaxReqSize is the maximum allowed payload size for an individual request in a
	// stream (client->server) in bytes. The size which is measured is the serialized
	// payload after per-message compression (but before stream compression) in bytes.
	// The actual value used is the minimum of the value specified here and the value set

View on GitHub (pinned to 0c51461d27)