hyperledger/fabric · error

cannot load client cert for consenter %s:%d: %s

Error message

cannot load client cert for consenter %s:%d: %s

What it means

MarshalEtcdRaftMetadata converts an etcdraft.ConfigMetadata whose consenters hold local file paths for TLS certs into one holding raw cert bytes by reading the files with os.ReadFile. This error wraps the read failure for a consenter's client TLS cert, including the host, port, and underlying OS error. It surfaces during channel config creation/orderer group validation when the paths are wrong or unreadable.

Source

Thrown at common/channelconfig/util.go:319

		return nil, errors.Errorf("invalid configuration block, missing %s configuration group", ApplicationGroupKey)
	}

	cc, err := NewChannelConfig(configEnv.Config.ChannelGroup, bccsp)
	if err != nil {
		return nil, errors.WithMessage(err, "no valid channel configuration found")
	}
	return cc, nil
}

// MarshalEtcdRaftMetadata serializes etcd RAFT metadata.
func MarshalEtcdRaftMetadata(md *etcdraft.ConfigMetadata) ([]byte, error) {
	copyMd := proto.Clone(md).(*etcdraft.ConfigMetadata)
	for _, c := range copyMd.Consenters {
		// Expect the user to set the config value for client/server certs to the
		// path where they are persisted locally, then load these files to memory.
		clientCert, err := os.ReadFile(string(c.GetClientTlsCert()))
		if err != nil {
			return nil, fmt.Errorf("cannot load client cert for consenter %s:%d: %s", c.GetHost(), c.GetPort(), err)
		}
		c.ClientTlsCert = clientCert

		serverCert, err := os.ReadFile(string(c.GetServerTlsCert()))
		if err != nil {
			return nil, fmt.Errorf("cannot load server cert for consenter %s:%d: %s", c.GetHost(), c.GetPort(), err)
		}
		c.ServerTlsCert = serverCert
	}
	return proto.Marshal(copyMd)
}

// MarshalBFTOptions serializes smartbft options.
func MarshalBFTOptions(op *smartbft.Options) ([]byte, error) {
	if copyMd, ok := proto.Clone(op).(*smartbft.Options); ok {
		return proto.Marshal(copyMd)
	} else {
		return nil, errors.New("consenter options type mismatch")

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Fix the client_tls_cert path in the consenter entry (configtx.yaml / raft metadata) to point at an existing file readable by the process.
  2. Run from a working directory where the relative cert paths resolve, or switch to absolute paths.
  3. In containers, mount the certificate directory and verify with `ls -l <path>` inside the container.
  4. Check file permissions (the process user needs read access) and re-read the OS error embedded in the message for the exact cause (ENOENT, EACCES, EISDIR).

Example fix

// configtx.yaml before
Consenter:
  - Host: raft0
    Port: 7050
    ClientTLSCert: crypto-config/peerOrganizations/tls/server.crt
// after (path that exists relative to configtxgen cwd)
Consenter:
  - Host: raft0
    Port: 7050
    ClientTLSCert: ./crypto-config/ordererOrganizations/example.com/orderers/raft0.example.com/tls/server.crt
Defensive patterns

Strategy: validation

Validate before calling

import "os"

func validateConsenterCerts(md *etcdraft.ConfigMetadata) error {
	for _, c := range md.Consenters {
		if fi, err := os.Stat(string(c.GetClientTlsCert())); err != nil {
			return fmt.Errorf("client cert %q unreadable: %w", c.GetClientTlsCert(), err)
		} else if fi.IsDir() {
			return fmt.Errorf("client cert %q is a directory", c.GetClientTlsCert())
		}
	}
	return nil
}

Try / catch

// errors are returned, not panicked; wrap the call
md, err := channelconfig.MarshalEtcdRaftMetadata(metadata)
if err != nil {
	var pe *fs.PathError
	if errors.As(err, &pe) { /* fix cert path: %q -> %v */ }
	return fmt.Errorf("raft metadata: %w", err)
}

Prevention

When it happens

Trigger: NewOrdererGroup (or the unit test) invoked with etcdraft metadata where a consenter's ClientTlsCert field contains a path that does not exist, is a directory, or is unreadable by the process.

Common situations: Running configtxgen/orderer on a machine where the generated cert paths don't exist; relative paths resolved from a different working directory; containerized deployments where host paths were not mounted; permissions changed after cert rotation.

Related errors


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