{"record":{"id":"68f8cfdbd5bc1aa0","repo":"grpc/grpc-go","slug":"serverhandshake-is-not-supported-for-client-creden","errorCode":null,"errorMessage":"ServerHandshake is not supported for client credentials","messagePattern":"ServerHandshake is not supported for client credentials","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/xds/xds.go","lineNumber":161,"sourceCode":"\t\t}\n\tcase <-ctx.Done():\n\t\tconn.Close()\n\t\treturn nil, nil, ctx.Err()\n\t}\n\tinfo := credentials.TLSInfo{\n\t\tState: conn.ConnectionState(),\n\t\tCommonAuthInfo: credentials.CommonAuthInfo{\n\t\t\tSecurityLevel: credentials.PrivacyAndIntegrity,\n\t\t},\n\t\tSPIFFEID: credinternal.SPIFFEIDFromState(conn.ConnectionState()),\n\t}\n\treturn credinternal.WrapSyscallConn(rawConn, conn), info, nil\n}\n\n// ServerHandshake performs the TLS handshake on the server-side.\nfunc (c *credsImpl) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {\n\tif c.isClient {\n\t\treturn nil, nil, errors.New(\"ServerHandshake is not supported for client credentials\")\n\t}\n\n\t// An xds-enabled gRPC server wraps the underlying raw net.Conn in a type\n\t// that provides a way to retrieve `HandshakeInfo`, which contains the\n\t// certificate providers to be used during the handshake. If the net.Conn\n\t// passed to this function does not implement this interface, or if the\n\t// `HandshakeInfo` does not contain the information we are looking for, we\n\t// delegate the handshake to the fallback credentials.\n\thiConn, ok := rawConn.(interface {\n\t\tXDSHandshakeInfo() (*grpcsync.RefCounted[xdsinternal.HandshakeInfo], error)\n\t})\n\tif !ok {\n\t\treturn c.fallback.ServerHandshake(rawConn)\n\t}\n\thi, err := hiConn.XDSHandshakeInfo()\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/credentials/xds/xds.go#L143-L179","documentation":"Returned by credsImpl.ServerHandshake (credentials/xds/xds.go:161) when c.isClient is true. Symmetric to error 28: the credsImpl rejects calling the server-side handshake method on a client-credentials instance. The check is the very first statement of ServerHandshake so it fails before any TLS state is touched.","triggerScenarios":"Creating credentials with xds.NewClientCredentials(...) and passing them to grpc.NewServer(grpc.Creds(...)) so the server tries ServerHandshake on client creds. Surfaces on the first inbound connection handshake.","commonSituations":"Reusing a client-credentials variable in server setup; full-duplex service that mixed up the two New* calls; refactor that merged client and server credential setup.","solutions":["Use xds.NewServerCredentials for servers and xds.NewClientCredentials for clients.","Verify the credentials passed to grpc.NewServer were produced by NewServerCredentials.","Keep client and server credential setup in separate functions/variables."],"exampleFix":"// before\ncreds, _ := xds.NewClientCredentials(xds.ClientOptions{FallbackCreds: insecure.NewCredentials()})\nsrv := grpc.NewServer(grpc.Creds(creds)) // ServerHandshake fails\n\n// after\ncreds, _ := xds.NewServerCredentials(xds.ServerOptions{FallbackCreds: insecure.NewCredentials()})\nsrv := grpc.NewServer(grpc.Creds(creds))","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// Mirror of error 28: guard by construction so server creds are never\n// handed to a client and vice-versa.\ntype serverXDS struct{ credentials.TransportCredentials }","tryCatchPattern":"// Surfaces on the first inbound handshake; prevent by construction (see tips).","preventionTips":["Keep server credential construction isolated from client credential construction.","Use distinct variable names and code reviews focused on the New*Client*/New*Server* pairing."],"tags":["go","grpc","security","xds","credentials","misuse"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}