grpc/grpc-go · error

xds: no peer certificates presented

Error message

xds: no peer certificates presented

What it means

Returned by the custom peer-cert verifier (buildVerifyFunc) when the peer presented zero raw certificates during the TLS handshake. Because InsecureSkipVerify is set so gRPC can run its own verification, an empty chain is caught explicitly here rather than by the stdlib. This typically means the peer sent a TLS alert instead of a certificate.

Source

Thrown at internal/credentials/xds/handshake_info.go:256

	if hi.identityProvider != nil {
		km, err := hi.identityProvider.KeyMaterial(ctx)
		if err != nil {
			return nil, fmt.Errorf("xds: fetching identity certificates from CertificateProvider failed: %v", err)
		}
		cfg.Certificates = km.Certs
	}

	if envconfig.XDSSNIEnabled && sni != "" {
		cfg.ServerName = sni
	}
	return cfg, nil
}

func (hi *HandshakeInfo) buildVerifyFunc(km *certprovider.KeyMaterial, isClient bool, sni string) func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
	return func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
		if len(rawCerts) == 0 {
			return fmt.Errorf("xds: no peer certificates presented")
		}
		// Parse all raw certificates presented by the peer.
		var certs []*x509.Certificate
		for _, rc := range rawCerts {
			cert, err := x509.ParseCertificate(rc)
			if err != nil {
				return err
			}
			certs = append(certs, cert)
		}

		// Build the intermediates list and verify that the leaf certificate is
		// signed by one of the root certificates. If a SPIFFE Bundle Map is
		// configured, it is used to get the root certs. Otherwise, the
		// configured roots in the root provider are used.
		intermediates := x509.NewCertPool()
		for _, cert := range certs[1:] {
			intermediates.AddCert(cert)

View on GitHub (pinned to 03255a9237)

Solutions

  1. Ensure the client is configured with a certificate when the server expects mTLS.
  2. Confirm the client's identityProvider is producing certs (see errors 250/254).
  3. If one-way TLS is intended, do not set requireClientCert or the SPIFFE verify callback on the server.
  4. Check for a middlebox or proxy terminating TLS before traffic reaches the gRPC server.
Defensive patterns

Strategy: try-catch

Try / catch

if err := verifyPeer(rawCerts); err != nil {
    if strings.Contains(err.Error(), "no peer certificates presented") {
        // client did not send a cert; fail closed unless one-way TLS is intended
    }
}

Prevention

When it happens

Trigger: buildVerifyFunc at handshake_info.go:255 receives rawCerts of length 0. Happens on the server when the client connects without a certificate while the server still wires up a VerifyPeerCertificate callback, or when the peer aborts the handshake early.

Common situations: Client misconfiguration (no client cert in an mTLS-required setup); network middlebox that strips the cert; client using TLS (not mTLS) against a server that configured tls.RequireAnyClientCert + custom verify; peer crashed mid-handshake.

Understand the failure class

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/d0e88f88e78e519f. Report an issue: GitHub.