{"record":{"id":"d0e88f88e78e519f","repo":"grpc/grpc-go","slug":"xds-no-peer-certificates-presented","errorCode":null,"errorMessage":"xds: no peer certificates presented","messagePattern":"xds: no peer certificates presented","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/credentials/xds/handshake_info.go","lineNumber":256,"sourceCode":"\n\tif hi.identityProvider != nil {\n\t\tkm, err := hi.identityProvider.KeyMaterial(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"xds: fetching identity certificates from CertificateProvider failed: %v\", err)\n\t\t}\n\t\tcfg.Certificates = km.Certs\n\t}\n\n\tif envconfig.XDSSNIEnabled && sni != \"\" {\n\t\tcfg.ServerName = sni\n\t}\n\treturn cfg, nil\n}\n\nfunc (hi *HandshakeInfo) buildVerifyFunc(km *certprovider.KeyMaterial, isClient bool, sni string) func(rawCerts [][]byte, _ [][]*x509.Certificate) error {\n\treturn func(rawCerts [][]byte, _ [][]*x509.Certificate) error {\n\t\tif len(rawCerts) == 0 {\n\t\t\treturn fmt.Errorf(\"xds: no peer certificates presented\")\n\t\t}\n\t\t// Parse all raw certificates presented by the peer.\n\t\tvar certs []*x509.Certificate\n\t\tfor _, rc := range rawCerts {\n\t\t\tcert, err := x509.ParseCertificate(rc)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcerts = append(certs, cert)\n\t\t}\n\n\t\t// Build the intermediates list and verify that the leaf certificate is\n\t\t// signed by one of the root certificates. If a SPIFFE Bundle Map is\n\t\t// configured, it is used to get the root certs. Otherwise, the\n\t\t// configured roots in the root provider are used.\n\t\tintermediates := x509.NewCertPool()\n\t\tfor _, cert := range certs[1:] {\n\t\t\tintermediates.AddCert(cert)","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/credentials/xds/handshake_info.go#L238-L274","documentation":"Raised inside the custom peer-cert verifier (buildVerifyFunc) when the rawCerts slice passed by the TLS stack is empty — the peer presented no certificate at all. With InsecureSkipVerify set and a custom verifier expecting a peer cert, this is fatal.","triggerScenarios":"Server-side: requireClientCert was effectively lowered such that a client connected without a cert and the verifier still ran; client-side: the server sent no certificate chain during the handshake; a TLS terminator upstream stripped the cert.","commonSituations":"ClientAuth misconfigured (e.g. tls.RequireAnyClientCert not set when SPIFFE bundle verification is active, or a plain TLS client hitting an mTLS-expecting path); a load balancer terminating TLS and forwarding plain; network middlebox stripping client certs.","solutions":["Require the peer to present a certificate: server-side use tls.RequireAnyClientCert (or RequireAndVerifyClientCert) so the handshake fails early if absent.","Ensure the client is configured with an identity provider when mTLS is required.","Remove any TLS-terminating proxy between the peers, or configure it to forward client certificates.","If using SPIFFE bundle verification on the server, confirm ClientAuth is dropped to RequireAnyClientCert as the code does in that branch."],"exampleFix":"// before: cfg.ClientAuth = tls.NoClientCert // peer sends nothing, verifier errors\n// after: cfg.ClientAuth = tls.RequireAnyClientCert","handlingStrategy":"validation","validationCode":"func requirePeerCerts(cfg *tls.Config) {\n    // for a server expecting mTLS/SPIFFE verification\n    cfg.ClientAuth = tls.RequireAnyClientCert // or RequireAndVerifyClientCert\n}","typeGuard":null,"tryCatchPattern":"In VerifyPeerCertificate, if len(rawCerts)==0 return a clear 'peer did not present a certificate' error and close the connection; log it as a policy violation. Do not fall back to unverified.","preventionTips":["Set ClientAuth >= RequireAnyClientCert whenever peer certs are expected.","Do not put a TLS-terminating proxy in front of an mTLS-verifying server unless it forwards client certs.","Test the handshake with a cert-less client to confirm it fails closed."],"tags":["grpc","xds","tls","mtls","handshake","security"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}