grpc/grpc-go · error
xdsclient: transport is nil
Error message
xdsclient: transport is nil
What it means
`newXDSChannel` (internal/xds/clients/xdsclient/channel.go:79) validates the xdsChannelOpts struct before constructing an ADS-backed channel. The `transport` field (a clients.Transport used to talk to the xDS server) is mandatory — without it the channel has no way to open the ADS stream. A nil transport fails the switch at channel.go:81-82.
Source
Thrown at internal/xds/clients/xdsclient/channel.go:82
// xdsChannelOpts holds the options for creating a new xdsChannel.
type xdsChannelOpts struct {
transport clients.Transport // Takes ownership of this transport.
serverConfig *ServerConfig // Configuration of the server to connect to.
clientConfig *Config // Complete xDS client configuration, used to decode resources.
eventHandler xdsChannelEventHandler // Callbacks for ADS stream events.
backoff func(int) time.Duration // Backoff function to use for stream retries. Defaults to exponential backoff, if unset.
watchExpiryTimeout time.Duration // Timeout for ADS resource watch expiry.
logPrefix string // Prefix to use for logging.
}
// newXDSChannel creates a new xdsChannel instance with the provided options.
// It performs basic validation on the provided options and initializes the
// xdsChannel with the necessary components.
func newXDSChannel(opts xdsChannelOpts) (*xdsChannel, error) {
switch {
case opts.transport == nil:
return nil, errors.New("xdsclient: transport is nil")
case opts.serverConfig == nil:
return nil, errors.New("xdsclient: serverConfig is nil")
case opts.clientConfig == nil:
return nil, errors.New("xdsclient: clientConfig is nil")
case opts.eventHandler == nil:
return nil, errors.New("xdsclient: eventHandler is nil")
}
xc := &xdsChannel{
transport: opts.transport,
serverConfig: opts.serverConfig,
clientConfig: opts.clientConfig,
eventHandler: opts.eventHandler,
closed: syncutil.NewEvent(),
}
l := grpclog.Component("xds")
logPrefix := opts.logPrefix + fmt.Sprintf("[xds-channel %p] ", xc)View on GitHub (pinned to 0c51461d27)
Solutions
- Ensure the transport is built (via the configured TransportBuilder) and passed into xdsChannelOpts.transport before the channel is created.
- Verify the TransportBuilder used to produce the transport is itself non-nil and returned no error.
- In tests, use the provided test helpers that construct a transport fake instead of a bare struct.
Example fix
// before
opts := xdsChannelOpts{serverConfig: sc, clientConfig: cc, eventHandler: h}
xc, err := newXDSChannel(opts) // err: transport is nil
// after
transport, err := transportBuilder.Build(sc.ServerIdentifier)
if err != nil { return err }
opts := xdsChannelOpts{transport: transport, serverConfig: sc, clientConfig: cc, eventHandler: h}
xc, err := newXDSChannel(opts) Defensive patterns
Strategy: validation
Validate before calling
// In your channel factory, validate transport before delegating.
func buildChannel(tb clients.TransportBuilder, sc *xdsclient.ServerConfig, cc *xdsclient.Config, h xdsChannelEventHandler) (*xdsChannel, error) {
if tb == nil {
return nil, errors.New("cannot build xDS channel: transport builder is nil")
}
transport, err := tb.Build(sc.ServerIdentifier)
if err != nil {
return nil, fmt.Errorf("transport build failed: %w", err)
}
if transport == nil {
return nil, errors.New("transport builder returned nil transport")
}
return newXDSChannel(xdsChannelOpts{transport: transport, serverConfig: sc, clientConfig: cc, eventHandler: h})
} Prevention
- Centralize xdsChannel construction so the transport is always built and checked in one place.
- Treat the TransportBuilder and the transport it produces as a single validation unit.
- In tests, use a fake transport rather than omitting the field.
When it happens
Trigger: Triggered when `newXDSChannel` is invoked (internally, by the higher-level xDS client while creating a channel for a server config) with `opts.transport == nil`. This is an internal-API programming error, not a runtime network condition.
Common situations: A custom xDS client implementation that does not build a transport before invoking the channel factory; a refactor that drops the transport assignment; tests that construct xdsChannelOpts with zero-values.
Related errors
- xdsclient: clientConfig is nil
- lrsclient: transport builder is nil
- xdsclient: serverConfig is nil
- xdsclient: eventHandler is nil
- xds_wrr_locality: invalid LBConfig: child policy field must
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/1583e98c87630f25.
Report an issue: GitHub.