grpc/grpc-go · warning
lrs: failed to receive first LoadStatsResponse
Error message
lrs: failed to receive first LoadStatsResponse: %v
What it means
Returned by recvFirstLoadStatsResponse when the first stream.Recv() on the LRS stream fails (lrs_stream.go:207). The runner logs it as a warning and retries with backoff, so this is a transient/recoverable condition rather than a fatal one.
Solutions
- Check connectivity and TLS to the LRS server independently (grpcurl, openssl s_client).
- Confirm the target URI is an LRS-enabled endpoint (envoy.service.load_stats.v3.LoadReportingService).
- Raise gRPC verbosity to see the wrapped transport error and act on it.
- If transient, rely on the built-in exponential backoff - the runner will reconnect automatically.
Defensive patterns
Strategy: retry
Prevention
- Rely on the LRS runner's built-in exponential backoff - it reconnects automatically.
- Pre-validate TLS and connectivity to the LRS server before starting ReportLoad.
- Set GRPC_VERBOSITY and Component(xds) logging to capture the wrapped transport error.
When it happens
Trigger: The LRS server closes the StreamLoadStats stream before sending any LoadStatsResponse, or the underlying transport fails (TLS error, RST_STREAM, context cancel, deadline). The first Recv after sending the initial LoadStatsRequest returns a non-nil error.
Common situations: LRS server is not actually an LRS server (wrong URI); server rejects the node identity; TLS handshake to the LRS server fails; intermediate proxy times out idle streams; server-side bug drops the stream on first message.
Related errors
- failed to create a stream to external processor
- lrs: unexpected message type %T
- dns: record lookup error
- extproc: failed to create channel to the external processor…
- failed to receive ALTS handshaker response
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/036e7efe17671af4.
Report an issue: GitHub.
Appendix: source
Thrown at internal/xds/clients/lrsclient/lrs_stream.go:207
return err
}
err = stream.Send(msg)
if err == io.EOF {
return getStreamError(stream)
}
return err
}
// recvFirstLoadStatsResponse receives the first LoadStatsResponse from the LRS
// server. Returns the following:
// - a list of cluster names requested by the server or an empty slice if the
// server requested for load from all clusters
// - the load reporting interval, and
// - any error encountered
func (lrs *streamImpl) recvFirstLoadStatsResponse(stream clients.Stream) ([]string, time.Duration, error) {
r, err := stream.Recv()
if err != nil {
return nil, 0, fmt.Errorf("lrs: failed to receive first LoadStatsResponse: %v", err)
}
var resp v3lrspb.LoadStatsResponse
if err := proto.Unmarshal(r, &resp); err != nil {
if lrs.logger.V(2) {
lrs.logger.Infof("Failed to unmarshal response to LoadStatsResponse: %v", err)
}
return nil, time.Duration(0), fmt.Errorf("lrs: unexpected message type %T", r)
}
if lrs.logger.V(perRPCVerbosityLevel) {
lrs.logger.Infof("Received first LoadStatsResponse: %s", pretty.ToJSON(&resp))
}
internal := resp.GetLoadReportingInterval()
if internal.CheckValid() != nil {
return nil, 0, fmt.Errorf("lrs: invalid load_reporting_interval: %v", err)
}
loadReportingInterval := internal.AsDuration()
View on GitHub (pinned to 0c51461d27)