golang/go · error
tls: failed to parse client certificate: {err}
Error message
tls: failed to parse client certificate: {err} What it means
processCertsFromClient failed to ASN.1-parse one of the client's certificate bytes via x509.ParseCertificate. The bytes are not a valid X.509 certificate. The server alerts decode_error and surfaces the wrapped parse error.
Source
Thrown at src/crypto/tls/handshake_server.go:950
if _, err := hs.c.writeHandshakeRecord(finished, &hs.finishedHash); err != nil {
return err
}
copy(out, finished.verifyData)
return nil
}
// processCertsFromClient takes a chain of client certificates either from a
// certificateMsg message or a certificateMsgTLS13 message and verifies them.
func (c *Conn) processCertsFromClient(certificate Certificate) error {
certificates := certificate.Certificate
certs := make([]*x509.Certificate, len(certificates))
var err error
for i, asn1Data := range certificates {
if certs[i], err = x509.ParseCertificate(asn1Data); err != nil {
c.sendAlert(alertDecodeError)
return errors.New("tls: failed to parse client certificate: " + err.Error())
}
if certs[i].PublicKeyAlgorithm == x509.RSA {
n := certs[i].PublicKey.(*rsa.PublicKey).N.BitLen()
if max, ok := checkKeySize(n); !ok {
c.sendAlert(alertBadCertificate)
return fmt.Errorf("tls: client sent certificate containing RSA key larger than %d bits", max)
}
}
}
if len(certs) == 0 && requiresClientCert(c.config.ClientAuth) {
if c.vers == VersionTLS13 {
c.sendAlert(alertCertificateRequired)
} else {
c.sendAlert(alertHandshakeFailure)
}
return errors.New("tls: client didn't provide a certificate")
}View on GitHub (pinned to b6b368adc5)
Solutions
- Regenerate the client certificate with a known-good tool (openssl x509, cfssl) and ensure DER encoding is sent over the wire.
- Inspect the wrapped err to identify the ASN.1 failure point (e.g. 'sequence tag mismatch').
- Verify the certificate validates independently: openssl x509 -in client.crt -noout -text.
- If the client is third-party, request they resend a valid chain.
Example fix
// Before: client sent a PEM-encoded cert (wrong for TLS wire format)
block, _ := pem.Decode(pemBytes)
// forgot to use block.Bytes
// After: send DER bytes
cert := tls.Certificate{
Certificate: [][]byte{block.Bytes}, // DER, not PEM
PrivateKey: priv,
} Defensive patterns
Strategy: validation
Validate before calling
// Client: validate the cert parses as DER before adding to tls.Certificate.
for i, der := range derCerts {
if _, err := x509.ParseCertificate(der); err != nil {
return fmt.Errorf("cert[%d] invalid: %w", i, err)
}
}
cert := tls.Certificate{Certificate: derCerts, PrivateKey: priv} Try / catch
// Server: log the wrapped parse error for diagnosis.
if err != nil && strings.Contains(err.Error(), "failed to parse client certificate") {
log.Warn("client sent malformed cert", "err", err)
} Prevention
- Always DER-encode certificates for the TLS wire format (use block.Bytes from pem.Decode, not the raw PEM).
- Validate certs with openssl x509 before deploying.
- Run client cert generation through vetted tooling.
When it happens
Trigger: For each asn1Data entry in certificate.Certificate, x509.ParseCertificate returns an error — malformed DER, truncated bytes, wrong tag, or non-X.509 structure. The server sends alertDecodeError.
Common situations: A client sending a corrupt or truncated certificate chain, a misconfigured client presenting a PEM-encoded (not DER) certificate, a bug in certificate generation, or an attacker injecting garbage into the certificate field.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- tls: client certificate used with invalid signature algorith
- tls: invalid signature by the client certificate: {err}
- tls: client didn't provide a certificate
- client's certificate is not allowed in FIPS 140-3 mode
- tls: client certificate uses ML-DSA, which requires TLS 1.3
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/5a6decc2daf354a3.
Report an issue: GitHub.