grpc/grpc-go · error
xdsclient: clientConfig is nil
Error message
xdsclient: clientConfig is nil
What it means
`newXDSChannel` (channel.go:79) also requires `clientConfig` (*Config) — the full xDS client configuration used to decode resources. The channel uses it (including its Node proto) when building ADS requests and when interpreting resource responses. A nil clientConfig fails at channel.go:85-86.
Source
Thrown at internal/xds/clients/xdsclient/channel.go:86
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)
xc.logger = igrpclog.NewPrefixLogger(l, logPrefix)
if opts.backoff == nil {
opts.backoff = backoff.DefaultExponential.BackoffView on GitHub (pinned to 0c51461d27)
Solutions
- Always pass the XDSClient's stored *Config (c.config) into xdsChannelOpts.clientConfig.
- If you write your own channel factory, validate clientConfig is non-nil at the entry point.
- Cover channel construction with a unit test that asserts the error path for a nil clientConfig.
Example fix
// before
opts := xdsChannelOpts{transport: t, serverConfig: sc, eventHandler: h}
xc, err := newXDSChannel(opts) // err: clientConfig is nil
// after
opts := xdsChannelOpts{transport: t, serverConfig: sc, clientConfig: c.config, eventHandler: h}
xc, err := newXDSChannel(opts) Defensive patterns
Strategy: validation
Validate before calling
// Ensure the client's own config is wired through.
func (c *XDSClient) safeNewChannel(opts xdsChannelOpts) (*xdsChannel, error) {
if c.config == nil {
return nil, errors.New("cannot build xDS channel: client config is nil")
}
opts.clientConfig = c.config
return newXDSChannel(opts)
} Prevention
- Always forward the XDSClient's stored *Config when assembling xdsChannelOpts.
- Add a linter or code-review check that any code constructing xdsChannelOpts sets all four mandatory fields.
- Write a unit test that asserts each of the four nil-field error paths.
When it happens
Trigger: Triggered when `newXDSChannel` is invoked with `opts.clientConfig == nil`. As with the other channel.go guards, this is an internal-API misuse: the XDSClient is expected to forward its own *Config.
Common situations: A refactor that drops the clientConfig field when assembling xdsChannelOpts; a custom XDSClient implementation that does not hold a Config; tests that build opts manually without copying the client's Config.
Related errors
- xdsclient: transport 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/321db6ec7ac45b98.
Report an issue: GitHub.