grpc/grpc-go · error

unexpected message type %T

Error message

unexpected message type %T

What it means

Returned by adsStreamImpl.recvMessage when stream.Recv() succeeds but the bytes cannot be unmarshaled into envoy.service.discovery.v3.DiscoveryResponse. recv() then reports the error via onError (which sets streamEstablished=false after the first failure) and closes the stream; the runner re-runs with backoff if no message was ever received.

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 03255a9237)

Solutions

  1. Confirm the server implements the v3 AggregatedDiscoveryService and that the client and server agree on the xDS major version.
  2. Use grpcurl to call StreamAggregatedResources and verify the returned bytes unmarshal as DiscoveryResponse.
  3. Remove any intermediating proxy that may rewrite the body.
  4. Upgrade/align go-control-plane and grpc-go versions on client and server.
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: The ADS management server sends a message on '/envoy.service.discovery.v3.AggregatedDiscoveryService/StreamAggregatedResources' that is not a valid DiscoveryResponse protobuf — e.g. a v2 DiscoveryResponse, an HTML/proxy error body, or a garbage frame.

Common situations: Version skew between client (v3) and server (v2), a non-xDS service behind the same URI, a proxy/LB corrupting the HTTP/2 body, or an in-progress server upgrade emitting a transient malformed frame.

Related errors


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