grpc/grpc-go · error
ServerHandshake is not supported for client credentials
Error message
ServerHandshake is not supported for client credentials
What it means
Returned by credsImpl.ServerHandshake when the credentials were created for client-side use (NewClientCredentials) but ServerHandshake is called on them. This is the mirror of the client-handshake guard: xDS credentials are directional and the isClient flag is true, so the guard at line 160-161 fires.
Solutions
- Use xds.NewServerCredentials for grpc.NewServer and xds.NewClientCredentials for grpc.Dial.
- Separate client and server credential construction into distinct functions or variables to prevent confusion.
Example fix
// before clientCreds, _ := xds.NewClientCredentials(opts) srv := grpc.NewServer(grpc.Creds(clientCreds)) // error // after serverCreds, _ := xds.NewServerCredentials(serverOpts) srv := grpc.NewServer(grpc.Creds(serverCreds))
Defensive patterns
Strategy: validation
Validate before calling
// Ensure server-side xDS credentials are used for NewServer:
serverCreds, err := xds.NewServerCredentials(serverOpts)
if err != nil { return err }
srv := grpc.NewServer(grpc.Creds(serverCreds)) Type guard
// No public field to check; prevent misuse by isolating server credential construction.
Prevention
- Never pass client credentials to grpc.NewServer.
- Keep server credential construction in the server setup function only.
- Review test harnesses that reuse credential objects.
When it happens
Trigger: A credentials instance created via xds.NewClientCredentials is passed to grpc.NewServer (which calls ServerHandshake), or is otherwise used in a server context.
Common situations: Developers pass client credentials to a gRPC server, often by sharing or mislabeling a credential variable. Also occurs in bidirectional test setups that reuse one credential object.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- ClientHandshake() is not supported for server credentials
- failed to build call credentials from bootstrap for
- failed to build credentials bundle from bootstrap for
- missing fallback credentials
- server handshake is not supported by xDS client TLS…
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/68f8cfdbd5bc1aa0.
Report an issue: GitHub.
Appendix: source
Thrown at credentials/xds/xds.go:161
}
case <-ctx.Done():
conn.Close()
return nil, nil, ctx.Err()
}
info := credentials.TLSInfo{
State: conn.ConnectionState(),
CommonAuthInfo: credentials.CommonAuthInfo{
SecurityLevel: credentials.PrivacyAndIntegrity,
},
SPIFFEID: credinternal.SPIFFEIDFromState(conn.ConnectionState()),
}
return credinternal.WrapSyscallConn(rawConn, conn), info, nil
}
// ServerHandshake performs the TLS handshake on the server-side.
func (c *credsImpl) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
if c.isClient {
return nil, nil, errors.New("ServerHandshake is not supported for client credentials")
}
// An xds-enabled gRPC server wraps the underlying raw net.Conn in a type
// that provides a way to retrieve `HandshakeInfo`, which contains the
// certificate providers to be used during the handshake. If the net.Conn
// passed to this function does not implement this interface, or if the
// `HandshakeInfo` does not contain the information we are looking for, we
// delegate the handshake to the fallback credentials.
hiConn, ok := rawConn.(interface {
XDSHandshakeInfo() (*grpcsync.RefCounted[xdsinternal.HandshakeInfo], error)
})
if !ok {
return c.fallback.ServerHandshake(rawConn)
}
hi, err := hiConn.XDSHandshakeInfo()
if err != nil {
return nil, nil, err
}View on GitHub (pinned to 0c51461d27)