hyperledger/fabric · error

parsing tls server cert of %s:%d

Error message

parsing tls server cert of %s:%d

What it means

validateConsenterTLSCerts then parses the consenter's server TLS cert; failure is wrapped with 'parsing tls server cert of <host>:<port>'. As with the client cert, this indicates the server_tls_cert bytes in the etcdraft consenter config are not decodable PEM/DER X.509 data. The error surfaces during VerifyConfigMetadata or ValidateConsensusMetadata of the channel config.

Source

Thrown at orderer/consensus/etcdraft/util.go:329

		Roots:         tlsRoots,
		Intermediates: tlsIntermediates,
		KeyUsages: []x509.ExtKeyUsage{
			x509.ExtKeyUsageClientAuth,
			x509.ExtKeyUsageServerAuth,
		},
	}, nil
}

// validateConsenterTLSCerts decodes PEM cert, parses and validates it.
func validateConsenterTLSCerts(c *etcdraft.Consenter, opts x509.VerifyOptions, ignoreExpiration bool) error {
	clientCert, err := parseCertificateFromBytes(c.GetClientTlsCert())
	if err != nil {
		return errors.Wrapf(err, "parsing tls client cert of %s:%d", c.GetHost(), c.GetPort())
	}

	serverCert, err := parseCertificateFromBytes(c.GetServerTlsCert())
	if err != nil {
		return errors.Wrapf(err, "parsing tls server cert of %s:%d", c.GetHost(), c.GetPort())
	}

	verify := func(certType string, cert *x509.Certificate, opts x509.VerifyOptions) error {
		if _, err := cert.Verify(opts); err != nil {
			if validationRes, ok := err.(x509.CertificateInvalidError); !ok || (!ignoreExpiration || validationRes.Reason != x509.Expired) {
				return errors.Wrapf(err, "verifying tls %s cert with serial number %d", certType, cert.SerialNumber)
			}
		}
		return nil
	}

	if err := verify("client", clientCert, opts); err != nil {
		return err
	}
	if err := verify("server", serverCert, opts); err != nil {
		return err
	}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Set the consenter's server_tls_cert to the base64-encoded tls/server.crt (-----BEGIN CERTIFICATE-----) and update channel config
  2. Verify the two cert fields aren't swapped: parse both with openssl x509 and compare with the node's actual TLS files
  3. Check the cert file wasn't truncated (should contain header, body, footer lines) and re-encode with base64 -w0
  4. Regenerate the node's TLS material and rebuild the config update transaction

Example fix

// before: base64 of key file
server_tls_cert: "$(base64 orderer.example.com/tls/server.key)"
// after
server_tls_cert: "$(base64 orderer.example.com/tls/server.crt)"
Defensive patterns

Strategy: validation

Validate before calling

// validate each consenter's server TLS cert before config update
for _, c := range metadata.Consenters {
    if err := validateTLSCertBytes(c.ServerTlsCert); err != nil {
        return fmt.Errorf("consenter %s:%d has invalid server tls cert: %w", c.Host, c.Port, err)
    }
}

Type guard

func hasValidServerTLSCert(c *etcdraft.Consenter) bool {
    block, _ := pem.Decode(c.GetServerTlsCert())
    return block != nil && block.Type == "CERTIFICATE"
}

Try / catch

if err := ValidateConsensusMetadata(m, l, nil); err != nil {
    if strings.Contains(err.Error(), "parsing tls server cert of") {
        return fmt.Errorf("a consenter's server_tls_cert is malformed: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Channel config update for an etcdraft consenter whose GetServerTlsCert() is empty, non-PEM, or contains bytes that fail x509.ParseCertificate; the consenter is identified by host:port in the wrapped message.

Common situations: operator swapped the client and server cert fields; server_tls_cert contains the private key; cert pasted without proper base64 encoding; file truncated during copy into configtx.yaml.

Understand the failure class

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/28d49972f3504f3e. Report an issue: GitHub.