{"record":{"id":"1583e98c87630f25","repo":"grpc/grpc-go","slug":"xdsclient-transport-is-nil","errorCode":null,"errorMessage":"xdsclient: transport is nil","messagePattern":"xdsclient: transport is nil","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/clients/xdsclient/channel.go","lineNumber":82,"sourceCode":"\n// xdsChannelOpts holds the options for creating a new xdsChannel.\ntype xdsChannelOpts struct {\n\ttransport          clients.Transport       // Takes ownership of this transport.\n\tserverConfig       *ServerConfig           // Configuration of the server to connect to.\n\tclientConfig       *Config                 // Complete xDS client configuration, used to decode resources.\n\teventHandler       xdsChannelEventHandler  // Callbacks for ADS stream events.\n\tbackoff            func(int) time.Duration // Backoff function to use for stream retries. Defaults to exponential backoff, if unset.\n\twatchExpiryTimeout time.Duration           // Timeout for ADS resource watch expiry.\n\tlogPrefix          string                  // Prefix to use for logging.\n}\n\n// newXDSChannel creates a new xdsChannel instance with the provided options.\n// It performs basic validation on the provided options and initializes the\n// xdsChannel with the necessary components.\nfunc newXDSChannel(opts xdsChannelOpts) (*xdsChannel, error) {\n\tswitch {\n\tcase opts.transport == nil:\n\t\treturn nil, errors.New(\"xdsclient: transport is nil\")\n\tcase opts.serverConfig == nil:\n\t\treturn nil, errors.New(\"xdsclient: serverConfig is nil\")\n\tcase opts.clientConfig == nil:\n\t\treturn nil, errors.New(\"xdsclient: clientConfig is nil\")\n\tcase opts.eventHandler == nil:\n\t\treturn nil, errors.New(\"xdsclient: eventHandler is nil\")\n\t}\n\n\txc := &xdsChannel{\n\t\ttransport:    opts.transport,\n\t\tserverConfig: opts.serverConfig,\n\t\tclientConfig: opts.clientConfig,\n\t\teventHandler: opts.eventHandler,\n\t\tclosed:       syncutil.NewEvent(),\n\t}\n\n\tl := grpclog.Component(\"xds\")\n\tlogPrefix := opts.logPrefix + fmt.Sprintf(\"[xds-channel %p] \", xc)","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/xds/clients/xdsclient/channel.go#L64-L100","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nopts := xdsChannelOpts{serverConfig: sc, clientConfig: cc, eventHandler: h}\nxc, err := newXDSChannel(opts) // err: transport is nil\n\n// after\ntransport, err := transportBuilder.Build(sc.ServerIdentifier)\nif err != nil { return err }\nopts := xdsChannelOpts{transport: transport, serverConfig: sc, clientConfig: cc, eventHandler: h}\nxc, err := newXDSChannel(opts)","handlingStrategy":"validation","validationCode":"// In your channel factory, validate transport before delegating.\nfunc buildChannel(tb clients.TransportBuilder, sc *xdsclient.ServerConfig, cc *xdsclient.Config, h xdsChannelEventHandler) (*xdsChannel, error) {\n    if tb == nil {\n        return nil, errors.New(\"cannot build xDS channel: transport builder is nil\")\n    }\n    transport, err := tb.Build(sc.ServerIdentifier)\n    if err != nil {\n        return nil, fmt.Errorf(\"transport build failed: %w\", err)\n    }\n    if transport == nil {\n        return nil, errors.New(\"transport builder returned nil transport\")\n    }\n    return newXDSChannel(xdsChannelOpts{transport: transport, serverConfig: sc, clientConfig: cc, eventHandler: h})\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["grpc","xds","xdschannel","config-validation","programming-error"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}