{"record":{"id":"de936816f646bb8b","repo":"grpc/grpc-go","slug":"unexpected-message-type-t","errorCode":null,"errorMessage":"unexpected message type %T","messagePattern":"unexpected message type %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/xds/clients/xdsclient/ads_stream.go","lineNumber":495,"sourceCode":"\t\t\t// know that it's invalid.  But we shouldn't ACK either, because we\n\t\t\t// don't know that it is valid.\n\t\t\ts.logger.Warningf(\"%v\", nackErr)\n\t\t\tcontinue\n\t\t}\n\n\t\ts.onRecv(stream, resourceNames, url, version, nonce, nackErr)\n\t}\n}\n\nfunc (s *adsStreamImpl) recvMessage(stream clients.Stream) (resources []*anypb.Any, url, version, nonce string, err error) {\n\tr, err := stream.Recv()\n\tif err != nil {\n\t\treturn nil, \"\", \"\", \"\", err\n\t}\n\tvar resp v3discoverypb.DiscoveryResponse\n\tif err := proto.Unmarshal(r, &resp); err != nil {\n\t\ts.logger.Infof(\"Failed to unmarshal response to DiscoveryResponse: %v\", err)\n\t\treturn nil, \"\", \"\", \"\", fmt.Errorf(\"unexpected message type %T\", r)\n\t}\n\tif s.logger.V(perRPCVerbosityLevel) {\n\t\ts.logger.Infof(\"ADS response received: %v\", pretty.ToJSON(&resp))\n\t} else if s.logger.V(2) {\n\t\ts.logger.Infof(\"ADS response received for type %q, version %q, nonce %q\", resp.GetTypeUrl(), resp.GetVersionInfo(), resp.GetNonce())\n\t}\n\treturn resp.GetResources(), resp.GetTypeUrl(), resp.GetVersionInfo(), resp.GetNonce(), nil\n}\n\n// onRecv is invoked when a response is received from the server. The arguments\n// passed to this method correspond to the most recently received response.\n//\n// It performs the following actions:\n//   - updates resource type specific state\n//   - updates resource specific state for resources in the response\n//   - sends an ACK or NACK to the server based on the response\nfunc (s *adsStreamImpl) onRecv(stream clients.Stream, names []string, url, version, nonce string, nackErr error) {\n\ts.mu.Lock()","sourceCodeStart":477,"sourceCodeEnd":513,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/xds/clients/xdsclient/ads_stream.go#L477-L513","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the management server implements the v3 xDS protocol (envoy.service.discovery.v3.AggregatedDiscoveryService).","Point the bootstrap xds_server.uri at a real xDS endpoint.","Increase logging verbosity to capture the raw bytes and confirm a version mismatch.","If a v2 server is unavoidable, use a client version that supports v2 or deploy a v2-to-v3 translating control plane."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Point the bootstrap xds_server at a v3-capable management server.","Verify the endpoint with grpcurl listing the ADS service.","The ADS runner reconnects with backoff automatically - no caller action needed for transient cases."],"tags":["xds","ads","protobuf","version-mismatch","stream"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}