grpc/grpc-go · error

no SubConn is available

Error message

no SubConn is available

What it means

Thrown by the RBAC HTTP filter builder's ParseFilterConfigOverride when the per-route override proto.Message passed by the xDS resolver is nil. The xDS HTTP filter framework requires every registered filter to handle overrides; the RBAC builder rejects nil outright rather than silently treating it as 'no override.' This prevents ambiguous state where a route reference is present in the LDS/RDS resource but carries no actual configuration payload.

Solutions

  1. Inspect the RDS resource for the route in question and confirm the http_filters per-filter-config entry for type envoy.extensions.filters.http.rbac.v3.RBACPerRoute has a non-nil typed_config with a valid type_url.
  2. If no per-route RBAC override is intended for that route, remove the RBACPerRoute entry from the route's typed_per_filter_config map entirely rather than leaving a nil placeholder.
  3. Upgrade the control plane and grpc-go to compatible xDS schema versions so the per-filter-config field is always populated with a well-formed Any.

Example fix

// before (control plane route config):
route:
  typed_per_filter_config:
    "envoy.filters.http.rbac":
      // missing @type body -> nil Any -> error

// after:
route:
  typed_per_filter_config:
    "envoy.filters.http.rbac":
      "@type": type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBACPerRoute
      rbac:
        rules:
          action: ALLOW
          policies:
            allow-all:
              permissions: [{any: true}]
              principals: [{any: true}]
Defensive patterns

Strategy: validation

Validate before calling

// Before calling the xDS filter pipeline, validate per-route override configs:
for routeName, route := range routeConfig.GetVirtualHosts()[0].GetRoutes() {
    for filterName, cfg := range route.GetTypedPerFilterConfig() {
        if strings.Contains(filterName, "rbac") && cfg == nil {
            return fmt.Errorf("route %s has nil RBAC per-filter override", routeName)
        }
        if cfg != nil && cfg.TypeUrl == "" {
            return fmt.Errorf("route %s RBAC override has empty type_url", routeName)
        }
    }
}

Type guard

func isNonNilAny(msg proto.Message) bool {
    if msg == nil {
        return false
    }
    any, ok := msg.(*anypb.Any)
    return ok && any != nil && any.TypeUrl != ""
}

Prevention

When it happens

Trigger: A control plane sends a RouteConfiguration (RDS) whose RouteAction references an RBACPerRoute typed per-filter-config entry, but the Any-wrapped message for that entry is nil or was stripped during proto marshalling. Also triggered if the xdsclient's unmarshalling code passes a nil override to httpfilter.ParseFilterConfigOverride due to a missing typed_config field in the per-filter-config map.

Common situations: Misconfigured Istio EnvoyFilter or Traffic Director route rule that specifies an RBAC per-route override key but omits the config body. Proto serialization round-trips that drop nil oneof fields. Control plane version mismatch where the per-filter-config schema expects a field the older client does not populate.

Related errors


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

Appendix: source

Thrown at balancer/balancer.go:268

	// Err is the rpc error the RPC finished with. It could be nil.
	Err error
	// Trailer contains the metadata from the RPC's trailer, if present.
	Trailer metadata.MD
	// BytesSent indicates if any bytes have been sent to the server.
	BytesSent bool
	// BytesReceived indicates if any byte has been received from the server.
	BytesReceived bool
	// ServerLoad is the load received from server. It's usually sent as part of
	// trailing metadata.
	//
	// The only supported type now is *orca_v3.LoadReport.
	ServerLoad any
}

var (
	// ErrNoSubConnAvailable indicates no SubConn is available for pick().
	// gRPC will block the RPC until a new picker is available via UpdateState().
	ErrNoSubConnAvailable = errors.New("no SubConn is available")
	// ErrTransientFailure indicates all SubConns are in TransientFailure.
	// WaitForReady RPCs will block, non-WaitForReady RPCs will fail.
	//
	// Deprecated: return an appropriate error based on the last resolution or
	// connection attempt instead.  The behavior is the same for any non-gRPC
	// status error.
	ErrTransientFailure = errors.New("all SubConns are in TransientFailure")
)

// PickResult contains information related to a connection chosen for an RPC.
type PickResult struct {
	// SubConn is the connection to use for this pick, if its state is Ready.
	// If the state is not Ready, gRPC will block the RPC until a new Picker is
	// provided by the balancer (using ClientConn.UpdateState).  The SubConn
	// must be one returned by ClientConn.NewSubConn.
	SubConn SubConn

	// Done is called when the RPC is completed.  If the SubConn is not ready,

View on GitHub (pinned to 0c51461d27)