{"record":{"id":"0f12cb568aabd7cf","repo":"grpc/grpc-go","slug":"xdsclient-serverconfig-is-nil","errorCode":null,"errorMessage":"xdsclient: serverConfig is nil","messagePattern":"xdsclient: serverConfig is nil","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/clients/xdsclient/channel.go","lineNumber":84,"sourceCode":"type 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)\n\txc.logger = igrpclog.NewPrefixLogger(l, logPrefix)\n","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/xds/clients/xdsclient/channel.go#L66-L102","documentation":"Inside `newXDSChannel` (channel.go:79), after the transport check, the next mandatory field is `serverConfig` (*ServerConfig) describing which xDS management server to connect to. The channel needs the server's ServerIdentifier (address, etc.) and features to dial the right destination, so a nil value is rejected at channel.go:83-84.","triggerScenarios":"Triggered when `newXDSChannel` is called with `opts.serverConfig == nil` while building a channel for an authority. This is an internal contract violation: the caller (the XDSClient) is expected to resolve a server config before requesting a channel.","commonSituations":"An xDS authority/configuration that resolves to no servers (e.g. an authority name in the Config map but with empty XDSServers); a custom resource-type implementation that bypasses the normal resolution; misconstructed Config.Authorities map.","solutions":["Verify the Config.Authorities and Config.Servers entries used to resolve the serverConfig are non-empty and well-formed.","If you author a custom ResourceType or authority resolution path, ensure it always yields a non-nil *ServerConfig.","Add a sanity check that logs the serverConfig before calling newXDSChannel so the source of the nil is obvious."],"exampleFix":"// before\nopts := xdsChannelOpts{transport: t, clientConfig: cc, eventHandler: h}\nxc, err := newXDSChannel(opts) // err: serverConfig is nil\n\n// after\nsc, ok := resolveServerConfig(authorityName)\nif !ok || sc == nil { return fmt.Errorf(\"no server config for authority %q\", authorityName) }\nopts := xdsChannelOpts{transport: t, serverConfig: sc, clientConfig: cc, eventHandler: h}\nxc, err := newXDSChannel(opts)","handlingStrategy":"validation","validationCode":"func resolveAndValidateServerConfig(authorities map[string]xdsclient.Authority, authorityName string) (*xdsclient.ServerConfig, error) {\n    a, ok := authorities[authorityName]\n    if !ok {\n        return nil, fmt.Errorf(\"authority %q not configured\", authorityName)\n    }\n    if len(a.XDSServers) == 0 || a.XDSServers[0].ServerIdentifier.ServerURI == \"\" {\n        return nil, fmt.Errorf(\"authority %q has no xDS servers\", authorityName)\n    }\n    return &a.XDSServers[0], nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate the Config.Authorities and Config.Servers entries at construction time, not at channel-creation time.","Refuse to start an authority that has no resolvable server config.","Log the resolved ServerConfig before channel creation so the source of a nil is obvious."],"tags":["grpc","xds","xdschannel","config-validation","authority"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}