grpc/grpc-go · warning
lrs: unexpected message type %T
Error message
lrs: unexpected message type %T
What it means
Returned by recvFirstLoadStatsResponse when the bytes received from the server cannot be unmarshaled into a LoadStatsResponse (lrs_stream.go:214). The implementation reports this as an unexpected message type even though the root cause is a protobuf unmarshal failure.
Solutions
- Confirm both client and server implement the v3 LRS service (envoy.service.load_stats.v3).
- Capture traffic (or raise perRPCVerbosityLevel logging) to inspect the actual bytes received.
- Point the client at the correct LRS endpoint/method.
Defensive patterns
Strategy: validation
Prevention
- Ensure the server implements the v3 LRS service (envoy.service.load_stats.v3).
- Version-test the server with grpcurl against the StreamLoadStats method before going live.
- Keep client and control-plane proto versions in sync.
When it happens
Trigger: stream.Recv() succeeds but proto.Unmarshal(r, &v3lrspb.LoadStatsResponse{}) fails - typically because the server sent a v2 message, a wrong-type response, or corrupted bytes. The function then returns fmt.Errorf("lrs: unexpected message type %T", r).
Common situations: Server and client disagree on the LRS proto version (v2 vs v3); the endpoint is not actually LRS (e.g. it returned a DiscoveryResponse); a buggy server serializes the wrong message type on the StreamLoadStats method.
Related errors
- unexpected message type %T
- lrs: failed to receive first LoadStatsResponse
- lrs: invalid load_reporting_interval
- extauthz: failed to unmarshal config
- extauthz: failed to unmarshal override config
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/f48d186522fd3bff.
Report an issue: GitHub.
Appendix: 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 0c51461d27)