hyperledger/fabric · error
cannot load server cert for consenter %s:%d: %s
Error message
cannot load server cert for consenter %s:%d: %s
What it means
MarshalEtcdRaftMetadata also reads each consenter's server TLS cert from its local path and inlines the bytes. This error wraps the os.ReadFile failure for the server TLS cert, naming the consenter host:port and the OS error. Same mechanics as the client-cert error but for ServerTlsCert.
Source
Thrown at common/channelconfig/util.go:325
}
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
- Correct the server_tls_cert path in the consenter config to an existing, readable file.
- Verify both ClientTLSCert and ServerTLSCert point to their intended files (they are often the same cert but distinct paths are validated separately).
- Mount/copy the server certs into the container or distribution package alongside the client certs.
- Check permissions and read the wrapped OS error for the precise failure (ENOENT vs EACCES).
Example fix
// before ClientTLSCert: ./tls/server.crt ServerTLSCert: ./tls/clint.crt # typo // after ClientTLSCert: ./tls/server.crt ServerTLSCert: ./tls/server.crt
Defensive patterns
Strategy: validation
Validate before calling
func validateServerCerts(md *etcdraft.ConfigMetadata) error {
for _, c := range md.Consenters {
if _, err := os.Stat(string(c.GetServerTlsCert())); err != nil {
return fmt.Errorf("server cert %q for %s:%d unreadable: %w", c.GetServerTlsCert(), c.GetHost(), c.GetPort(), err)
}
}
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 server cert path */ }
return fmt.Errorf("raft metadata: %w", err)
} Prevention
- Validate both ClientTLSCert and ServerTLSCert paths — they are read independently
- Copy both certs into container images during build
- Use consistent, absolute paths generated from one template to avoid typos
- Re-check paths after cert rotation or directory moves
When it happens
Trigger: NewOrdererGroup invoked with etcdraft metadata where a consenter's ServerTlsCert path is missing, unreadable, or points to a directory — reached only after the client cert read succeeded.
Common situations: Client and server cert paths mixed up in configtx.yaml; only one of the two certs copied into a container image; cert file deleted/moved during rotation; typo in the server cert filename.
Related errors
- cannot load client 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/7c0a629b4fd6ec8d.
Report an issue: GitHub.