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

  1. Correct the server_tls_cert path in the consenter config to an existing, readable file.
  2. Verify both ClientTLSCert and ServerTLSCert point to their intended files (they are often the same cert but distinct paths are validated separately).
  3. Mount/copy the server certs into the container or distribution package alongside the client certs.
  4. 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

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


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