grpc/grpc-go · warning

unexpected message type %T

Error message

unexpected message type %T

What it means

Returned by adsStreamImpl.recvMessage when proto.Unmarshal of the received bytes into DiscoveryResponse fails (ads_stream.go:495). As with the LRS variant, the error is reported as an unexpected message type even though the real cause is a protobuf deserialization failure.

Solutions

  1. Verify the management server implements the v3 xDS protocol (envoy.service.discovery.v3.AggregatedDiscoveryService).
  2. Point the bootstrap xds_server.uri at a real xDS endpoint.
  3. Increase logging verbosity to capture the raw bytes and confirm a version mismatch.
  4. If a v2 server is unavoidable, use a client version that supports v2 or deploy a v2-to-v3 translating control plane.
Defensive patterns

Strategy: retry

Prevention

When it happens

Trigger: On the ADS stream, stream.Recv() returns bytes that do not unmarshal as envoy.service.discovery.v3.DiscoveryResponse. recv returns this error and the recv loop treats it as a stream failure, triggers onError, and reconnects with backoff.

Common situations: xDS server speaks v2 (envoy.api.v2.DiscoveryResponse) while client expects v3; server is not actually an xDS management server; proxy corrupted the frame; version skew between client and control plane.

Related errors


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

Appendix: source

Thrown at internal/xds/clients/xdsclient/ads_stream.go:495

			// know that it's invalid.  But we shouldn't ACK either, because we
			// don't know that it is valid.
			s.logger.Warningf("%v", nackErr)
			continue
		}

		s.onRecv(stream, resourceNames, url, version, nonce, nackErr)
	}
}

func (s *adsStreamImpl) recvMessage(stream clients.Stream) (resources []*anypb.Any, url, version, nonce string, err error) {
	r, err := stream.Recv()
	if err != nil {
		return nil, "", "", "", err
	}
	var resp v3discoverypb.DiscoveryResponse
	if err := proto.Unmarshal(r, &resp); err != nil {
		s.logger.Infof("Failed to unmarshal response to DiscoveryResponse: %v", err)
		return nil, "", "", "", fmt.Errorf("unexpected message type %T", r)
	}
	if s.logger.V(perRPCVerbosityLevel) {
		s.logger.Infof("ADS response received: %v", pretty.ToJSON(&resp))
	} else if s.logger.V(2) {
		s.logger.Infof("ADS response received for type %q, version %q, nonce %q", resp.GetTypeUrl(), resp.GetVersionInfo(), resp.GetNonce())
	}
	return resp.GetResources(), resp.GetTypeUrl(), resp.GetVersionInfo(), resp.GetNonce(), nil
}

// onRecv is invoked when a response is received from the server. The arguments
// passed to this method correspond to the most recently received response.
//
// It performs the following actions:
//   - updates resource type specific state
//   - updates resource specific state for resources in the response
//   - sends an ACK or NACK to the server based on the response
func (s *adsStreamImpl) onRecv(stream clients.Stream, names []string, url, version, nonce string, nackErr error) {
	s.mu.Lock()

View on GitHub (pinned to 0c51461d27)