{"record":{"id":"0a95b4133d97ce65","repo":"grpc/grpc-go","slug":"clienthandshake-is-not-supported-for-server-cred","errorCode":null,"errorMessage":"ClientHandshake() is not supported for server credentials","messagePattern":"ClientHandshake\\(\\) is not supported for server credentials","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/xds/xds.go","lineNumber":96,"sourceCode":"\n// credsImpl is an implementation of the credentials.TransportCredentials\n// interface which uses xDS APIs to fetch its security configuration.\ntype credsImpl struct {\n\tisClient bool\n\tfallback credentials.TransportCredentials\n}\n\n// ClientHandshake performs the TLS handshake on the client-side.\n//\n// It looks for the presence of a HandshakeInfo value in the passed in context\n// (added using a call to NewContextWithHandshakeInfo()), and retrieves identity\n// and root certificates from there. It also retrieves a list of acceptable SANs\n// and uses a custom verification function to validate the certificate presented\n// by the peer. It uses fallback credentials if no HandshakeInfo is present in\n// the passed in context.\nfunc (c *credsImpl) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {\n\tif !c.isClient {\n\t\treturn nil, nil, errors.New(\"ClientHandshake() is not supported for server credentials\")\n\t}\n\n\t// The clusterimpl balancer constructs a new HandshakeInfo using a call to\n\t// NewHandshakeInfo(), and then adds it to the attributes field of the\n\t// resolver.Address when handling calls to NewSubConn(). The transport layer\n\t// takes care of shipping these attributes in the context to this handshake\n\t// function. We first read the credentials.ClientHandshakeInfo type from the\n\t// context, which contains the attributes added by the clusterimpl balancer.\n\t// We then read the HandshakeInfo from the attributes to get to the actual\n\t// data that we need here for the handshake.\n\tchi := credentials.ClientHandshakeInfoFromContext(ctx)\n\t// If there are no attributes in the received context or the attributes does\n\t// not contain a HandshakeInfo, it could either mean that the user did not\n\t// specify an `xds` scheme in their dial target or that the xDS server did\n\t// not provide any security configuration. In both of these cases, we use\n\t// the fallback credentials specified by the user.\n\tif chi.Attributes == nil {\n\t\treturn c.fallback.ClientHandshake(ctx, authority, rawConn)","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/credentials/xds/xds.go#L78-L114","documentation":"Returned by credsImpl.ClientHandshake when the credentials were created for server-side use (NewServerCredentials) but ClientHandshake is called on them. xDS credentials are directional: client credentials perform client-side TLS handshakes and server credentials perform server-side handshakes; calling the wrong method is a programming error.","triggerScenarios":"A credentials instance created via xds.NewServerCredentials is passed to grpc.Dial (which calls ClientHandshake), or is otherwise used in a client context. The isClient flag is false, so the guard at line 95-96 fires.","commonSituations":"Developers confuse client and server xDS credential constructors, or share a single credentials variable between dial and server code. Also seen in test harnesses that reuse a credential instance for both directions.","solutions":["Use xds.NewClientCredentials for client-side (grpc.Dial) and xds.NewServerCredentials for server-side (grpc.NewServer).","Audit shared credential variables and split them into separate client and server instances."],"exampleFix":"// before\nserverCreds, _ := xds.NewServerCredentials(opts)\nconn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(serverCreds)) // error\n// after\nclientCreds, _ := xds.NewClientCredentials(clientOpts)\nconn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(clientCreds))","handlingStrategy":"validation","validationCode":"// Ensure client-side xDS credentials are used for Dial:\nclientCreds, err := xds.NewClientCredentials(clientOpts)\nif err != nil { return err }\n// Verify it is NOT a server credential by keeping client/server construction separate.","typeGuard":"// xDS credentials do not expose a public isClient flag.\n// Prevent misuse by keeping client and server credential construction in separate, clearly named functions.","tryCatchPattern":null,"preventionTips":["Never share a credential instance between Dial and NewServer.","Name variables clearly: clientXCreds vs serverXCreds.","Review test harnesses that reuse credential objects."],"tags":["go","grpc","xds","credentials","misuse"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}