grpc/grpc-go · error
lrs: unexpected message type %T
Error message
lrs: unexpected message type %T
What it means
Returned by recvFirstLoadStatsResponse when stream.Recv() succeeds but proto.Unmarshal of the bytes into envoy.service.load_stats.v3.LoadStatsResponse fails. This means the server sent bytes on the LRS stream that are not a valid LoadStatsResponse protobuf. The runner treats it as a stream failure and retries with backoff.
Source
Thrown at internal/xds/clients/lrsclient/lrs_stream.go:214
}
// 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()
clusters := resp.Clusters
if resp.SendAllClusters {
// Return an empty slice to send stats for all clusters.
clusters = []string{}
}
return clusters, loadReportingInterval, nilView on GitHub (pinned to 03255a9237)
Solutions
- Confirm the remote is an xDS v3 management server that implements envoy.service.load_stats.v3.LoadReportingService.
- Capture the stream with grpcurl --protocol h2 against the LRS service path to see what the server actually returns.
- Check for an intermediating proxy/LB that may rewrite the body, and bypass it to test.
- Ensure client and server use the same go-control-plane / grpc-go proto versions for load_stats/v3.
Defensive patterns
Strategy: validation
Prevention
- Point the LRS client only at servers implementing envoy.service.load_stats.v3.LoadReportingService.
- Keep client and server on the same xDS major version (v3).
- Avoid routing the LRS stream through proxies that rewrite bodies.
When it happens
Trigger: Connecting to an endpoint that speaks a different protocol on the StreamLoadStats path (e.g. an HTTP/1 error page, an ADS/LRS version mismatch, or a proxy injecting HTML), or a buggy/non-conformant LRS server that emits an unrelated protobuf on the stream.
Common situations: Pointing the LRS client at a v2 LRS server while using v3 protos, a load balancer/proxy returning a plaintext error body over the otherwise-HTTP/2 stream, or an Envoy/control-plane build that serializes a different message type for StreamLoadStats.
Related errors
- unexpected message type %T
- lrs: failed to receive first LoadStatsResponse: %v
- lrs: invalid load_reporting_interval: %v
- lrsclient: failed to create transport for server identifier
- rls_csp: error parsing config %v: %v
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/f48d186522fd3bff.
Report an issue: GitHub.