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

  1. Verify General.TLS.Certificate (and General.Cluster.ClientCertificate) point to a valid PEM file beginning with '-----BEGIN CERTIFICATE-----'.
  2. Re-encode: openssl x509 -in cert.crt -out cert.pem and redeploy the orderer with the corrected file.
  3. If injecting via env/Kubernetes config, ensure newlines are preserved (use | multiline YAML or proper file mounts, not flattened env values).
  4. 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

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

Related errors


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