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
- Fix the client_tls_cert path in the consenter entry (configtx.yaml / raft metadata) to point at an existing file readable by the process.
- Run from a working directory where the relative cert paths resolve, or switch to absolute paths.
- In containers, mount the certificate directory and verify with `ls -l <path>` inside the container.
- 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
- Use absolute paths for TLS certs in configtx.yaml
- Verify cert paths exist and are readable by the process user before generating genesis blocks
- In containers, mount the cert directory and verify inside the container
- Log the wrapped OS error (ENOENT/EACCES) to distinguish missing vs permission issues
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
- cannot load server cert for consenter %s:%d: %s
- error loading TLS root certificate (%s)
- parsing tls client cert of %s:%d
- parsing tls server cert of %s:%d
- error writing output
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/a3cfe696372b76f5.
Report an issue: GitHub.