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

  1. Ensure the transport is built (via the configured TransportBuilder) and passed into xdsChannelOpts.transport before the channel is created.
  2. Verify the TransportBuilder used to produce the transport is itself non-nil and returned no error.
  3. 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

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


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