hyperledger/fabric · error
client certificate isn't in PEM format: %v
Error message
client certificate isn't in PEM format: %v
What it means
NewBlockPuller validates the orderer's own TLS client certificate before building the cluster block puller. The certificate bytes in the dialer's SecOpts.Certificate must be PEM-encoded; if pem.Decode fails, the configured certificate is malformed and the puller cannot be created. The raw bytes are included in the error to aid diagnosis.
Source
Thrown at orderer/consensus/etcdraft/blockpuller.go:88
vb := cluster.BlockVerifierBuilder(bccsp)
return cluster.VerifyBlocksBFT(blocks, support.SignatureVerifier(), vb)
}
stdDialer := &cluster.StandardDialer{
Config: baseDialer.Config,
}
stdDialer.Config.AsyncConnect = false
stdDialer.Config.SecOpts.VerifyCertificate = nil
// Extract the TLS CA certs and endpoints from the configuration,
endpoints, err := EndpointconfigFromSupport(support, bccsp)
if err != nil {
return nil, err
}
der, _ := pem.Decode(stdDialer.Config.SecOpts.Certificate)
if der == nil {
return nil, errors.Errorf("client certificate isn't in PEM format: %v",
string(stdDialer.Config.SecOpts.Certificate))
}
logger := flogging.MustGetLogger("orderer.common.cluster.puller").With("channel", support.ChannelID())
myCert, err := x509.ParseCertificate(der.Bytes)
if err != nil {
logger.Warnf("Failed parsing my own TLS certificate: %v, therefore we may connect to our own endpoint when pulling blocks", err)
}
bp := &cluster.BlockPuller{
MyOwnTLSCert: myCert,
VerifyBlockSequence: verifyBlockSequence,
Logger: logger,
RetryTimeout: clusterConfig.ReplicationRetryTimeout,
MaxTotalBufferBytes: clusterConfig.ReplicationBufferSize,
FetchTimeout: clusterConfig.ReplicationPullTimeout,
Endpoints: endpoints,View on GitHub (pinned to 2736b63f8f)
Solutions
- Verify General.TLS.Certificate (and General.Cluster.ClientCertificate) point to a valid PEM file beginning with '-----BEGIN CERTIFICATE-----'.
- Re-encode: openssl x509 -in cert.crt -out cert.pem and redeploy the orderer with the corrected file.
- If injecting via env/Kubernetes config, ensure newlines are preserved (use | multiline YAML or proper file mounts, not flattened env values).
- Confirm the private key (TLS.PrivateKey) and cert are a matching pair with openssl x509 / openssl rsa modulus comparison.
Example fix
// before: DER or header-less cert loaded
SecOpts.Certificate = rawDerBytes
// after: proper PEM-encoded certificate
pemBytes, _ := os.ReadFile("orderer.crt") // starts with -----BEGIN CERTIFICATE-----
SecOpts.Certificate = pemBytes Defensive patterns
Strategy: validation
Validate before calling
func isPEMCert(b []byte) bool {
block, _ := pem.Decode(b)
if block == nil || block.Type != "CERTIFICATE" {
return false
}
_, err := x509.ParseCertificate(block.Bytes)
return err == nil
}
// call before building SecOpts: if !isPEMCert(certBytes) { fail fast } Type guard
func validPEMCertificate(data []byte) bool {
der, _ := pem.Decode(data)
return der != nil
} Prevention
- Mount TLS certs as files, never inline through env vars that strip newlines
- Validate PEMs at deploy time: openssl x509 -in cert.pem -noout
- Keep BEGIN/END headers intact in config maps
When it happens
Trigger: Starting an etcdraft chain (NewBlockPuller via EndpointconfigFromSupport flow) when generalTLS or cluster TLS client certificate material in configuration (General.TLS.Certificate / General.Cluster.ClientCertificate) is not valid PEM — e.g., raw DER bytes, base64-only content, or an empty/garbled file.
Common situations: Config map/env var corruption where newlines in the PEM got mangled (e.g., single-line env values), pasting a certificate without the BEGIN/END headers, or generating a cert and forgetting PEM encoding.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- invalid certificate DER
- failed to add ca-file PEM to cert pool
- loading client cert/key pair: %s
- cannot load client cert for consenter %s:%d: %s
- cannot load server cert for consenter %s:%d: %s
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/67e10ceecd912981.
Report an issue: GitHub.