grpc/grpc-go · error

spiffe: verify function has no valid input certificates

Error message

spiffe: verify function has no valid input certificates

What it means

Returned by the SPIFFE verify callback when the rawCerts slice received from the peer is empty (bundle.go:171). With InsecureSkipVerify=true and a custom VerifyPeerCertificate, an empty chain means there is nothing to validate, so the handshake fails fast rather than silently succeeding.

Solutions

  1. Ensure the xDS management server is configured for mTLS and will present its certificate chain.
  2. If the deployment intentionally uses one-way TLS, remove the spiffe_trust_bundle_map_file from the bootstrap so the standard RootCAs path is used.
  3. Check for TLS-terminating proxies in the path and reconfigure them to passthrough or to present the upstream chain.
Defensive patterns

Strategy: try-catch

Try / catch

// Distinguish 'no cert sent' from other handshake errors:
if err != nil && strings.Contains(err.Error(), "has no valid input certificates") {
    // server did not present a certificate; check mTLS config
}

Prevention

When it happens

Trigger: The TLS handshake completes at the record layer but the peer supplied zero certificates. This happens during ClientHandshake when the server does not present a chain (e.g. server uses PSK or is misconfigured to not request/send a certificate) and a SPIFFE bundle map is in use.

Common situations: Server is configured for one-way TLS but client expects mTLS; server certificate selection fails silently; intermediary (load balancer, sidecar) terminates TLS and does not forward the peer cert; wrong port reached.

Understand the failure class

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/8d3d48b27b0f2ece. Report an issue: GitHub.

Appendix: source

Thrown at internal/xds/bootstrap/tlscreds/bundle.go:171

	return errors.New("overriding server name is not supported by xDS client TLS credentials")
}

func (c *reloadingCreds) ServerHandshake(net.Conn) (net.Conn, credentials.AuthInfo, error) {
	return nil, nil, errors.New("server handshake is not supported by xDS client TLS credentials")
}

func buildSPIFFEVerifyFunc(spiffeBundleMap map[string]*spiffebundle.Bundle) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
	return func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
		rawCertList := make([]*x509.Certificate, len(rawCerts))
		for i, asn1Data := range rawCerts {
			cert, err := x509.ParseCertificate(asn1Data)
			if err != nil {
				return fmt.Errorf("spiffe: verify function could not parse input certificate: %v", err)
			}
			rawCertList[i] = cert
		}
		if len(rawCertList) == 0 {
			return fmt.Errorf("spiffe: verify function has no valid input certificates")
		}
		leafCert := rawCertList[0]
		roots, err := spiffe.GetRootsFromSPIFFEBundleMap(spiffeBundleMap, leafCert)
		if err != nil {
			return err
		}

		opts := x509.VerifyOptions{
			Roots:         roots,
			CurrentTime:   time.Now(),
			Intermediates: x509.NewCertPool(),
		}

		for _, cert := range rawCertList[1:] {
			opts.Intermediates.AddCert(cert)
		}
		// The verified chain is (surprisingly) unused.
		if _, err = rawCertList[0].Verify(opts); err != nil {

View on GitHub (pinned to 0c51461d27)