grpc/grpc-go · error

lrsclient: transport builder is nil

Error message

lrsclient: transport builder is nil

What it means

`lrsclient.New` (internal/xds/clients/lrsclient/lrsclient.go:62) constructs a Load Reporting Service client from a Config struct. The Config's `TransportBuilder` field (clients.TransportBuilder) is the only way the LRS client can build transports to the LRS server, so a nil value is treated as a programming error and rejected at line 63-64. The LRS client needs it to dial each configured server and open load-reporting streams.

Source

Thrown at internal/xds/clients/lrsclient/lrsclient.go:64

// LRSClient is an LRS (Load Reporting Service) client.
type LRSClient struct {
	transportBuilder clients.TransportBuilder
	node             clients.Node
	backoff          func(int) time.Duration // Backoff for LRS stream failures.
	logger           *igrpclog.PrefixLogger

	// The LRSClient owns a bunch of streams to individual LRS servers.
	//
	// Once all references to a stream are dropped, the stream is closed.
	mu         sync.Mutex
	lrsStreams map[clients.ServerIdentifier]*streamImpl // Map from server config to in-use streamImpls.
	lrsRefs    map[clients.ServerIdentifier]int         // Map from server config to number of references.
}

// New returns a new LRS Client configured with the provided config.
func New(config Config) (*LRSClient, error) {
	if config.TransportBuilder == nil {
		return nil, errors.New("lrsclient: transport builder is nil")
	}

	c := &LRSClient{
		transportBuilder: config.TransportBuilder,
		node:             config.Node,
		backoff:          defaultExponentialBackoff,
		lrsStreams:       make(map[clients.ServerIdentifier]*streamImpl),
		lrsRefs:          make(map[clients.ServerIdentifier]int),
	}
	c.logger = prefixLogger(c)
	return c, nil
}

// ReportLoad creates and returns a LoadStore for the caller to report loads
// using a LoadReportingStream.
//
// Caller must call Stop on the returned LoadStore when they are done reporting
// load to this server.

View on GitHub (pinned to 0c51461d27)

Solutions

  1. Set `config.TransportBuilder` to a valid `clients.TransportBuilder` implementation before calling lrsclient.New.
  2. If you used the older bootstrap-derived builder, ensure the bootstrap parsing step actually returned a non-nil builder; log it before passing.
  3. Add a constructor/factory for your Config that fails fast if the builder is not injected.

Example fix

// before
cfg := lrsclient.Config{Node: node}
client, err := lrsclient.New(cfg) // err: transport builder is nil

// after
builder := clients.NewTransportBuilder(transportOpts)
cfg := lrsclient.Config{Node: node, TransportBuilder: builder}
client, err := lrsclient.New(cfg)
Defensive patterns

Strategy: validation

Validate before calling

func newLRSClient(node clients.Node, tb clients.TransportBuilder) (*lrsclient.LRSClient, error) {
    if tb == nil {
        return nil, errors.New("lrsclient: a non-nil TransportBuilder is required")
    }
    return lrsclient.New(lrsclient.Config{Node: node, TransportBuilder: tb})
}

Prevention

When it happens

Trigger: Triggered by calling `lrsclient.New(config)` with `config.TransportBuilder == nil`. The guard at lrsclient.go:63 fires immediately, before any allocation.

Common situations: Building a Config struct but forgetting to set TransportBuilder (relying on a zero-value); refactoring code that previously supplied a default builder; using a builder that was never initialized because an earlier setup step failed silently.

Related errors


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