{"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":"error","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/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/xds/clients/xdsclient/ads_stream.go#L477-L513","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Confirm the server implements the v3 AggregatedDiscoveryService and that the client and server agree on the xDS major version.","Use grpcurl to call StreamAggregatedResources and verify the returned bytes unmarshal as DiscoveryResponse.","Remove any intermediating proxy that may rewrite the body.","Upgrade/align go-control-plane and grpc-go versions on client and server."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use a v3-conformant ADS management server.","Keep grpc-go/go-control-plane versions aligned between client and server.","Do not place a body-rewriting proxy in front of the ADS endpoint."],"tags":["grpc","xds","ads","protobuf","protocol-mismatch"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}