hyperledger/fabric · error
no TLS certificate sent
Error message
no TLS certificate sent
What it means
The orderer requires mutual TLS on cluster communication: the sender's TLS client certificate proves the sender's identity. util.ExtractRawCertificateFromContext(ctx) returned empty because the incoming gRPC request's context carried no client certificate, so the sender cannot be authenticated and the request is rejected.
Source
Thrown at orderer/common/cluster/comm.go:103
// requestContext identifies the sender and channel of the request and returns
// it wrapped in a requestContext
func (c *Comm) requestContext(ctx context.Context, msg proto.Message) (*requestContext, error) {
channel := c.ChanExt.TargetChannel(msg)
if channel == "" {
return nil, errors.Errorf("badly formatted message, cannot extract channel")
}
c.Lock.RLock()
mapping, exists := c.Chan2Members[channel]
c.Lock.RUnlock()
if !exists {
return nil, errors.Errorf("channel %s doesn't exist", channel)
}
cert := util.ExtractRawCertificateFromContext(ctx)
if len(cert) == 0 {
return nil, errors.Errorf("no TLS certificate sent")
}
stub := mapping.LookupByClientCert(cert)
if stub == nil {
return nil, errors.Errorf("certificate extracted from TLS connection isn't authorized")
}
return &requestContext{
channel: channel,
sender: stub.ID,
}, nil
}
// Remote obtains a RemoteContext linked to the destination node on the context
// of a given channel
func (c *Comm) Remote(channel string, id uint64) (*RemoteContext, error) {
c.Lock.RLock()
defer c.Lock.RUnlock()
View on GitHub (pinned to 2736b63f8f)
Solutions
- Enable mutual TLS: set General.TLS.ClientAuthRequired: true in orderer.yaml and configure the client cert on the sender side
- Ensure the sender's gRPC dial includes its TLS client keypair (tls.Config{Certificates: ...})
- Check no proxy/load balancer terminates TLS between sender and orderer, dropping client certs
- Confirm General.TLS.Enabled is true on both ends and certs are signed by the same CA
Example fix
// before (sender dials without client cert)
conn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
// after
cert, _ := tls.LoadX509KeyPair("client.crt", "client.key")
tlsCfg := &tls.Config{Certificates: []tls.Certificate{cert}, RootCAs: caPool}
conn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(credentials.NewTLS(tlsCfg))) Defensive patterns
Strategy: validation
Validate before calling
// Sender-side: fail fast if mutual TLS is not fully configured
func requireMutualTLS(cfg *tls.Config) error {
if cfg == nil || len(cfg.Certificates) == 0 {
return errors.New("client certificate required for cluster communication")
}
if cfg.RootCAs == nil { return errors.New("server CA pool required") }
return nil
} Try / catch
if _, err := comm.Remote(channel, id); err != nil {
if strings.Contains(err.Error(), "no TLS certificate sent") {
// reconfigure TLS creds and redial
creds := credentials.NewTLS(loadMutualTLSConfig())
conn, err = grpc.Dial(addr, grpc.WithTransportCredentials(creds))
}
} Prevention
- Set General.TLS.ClientAuthRequired: true and provision client certs on every orderer
- Always pass the client keypair in the gRPC dial's tls.Config
- Avoid TLS-terminating proxies in front of cluster ports
- Rotate certs on both ends in lockstep with channel config updates
When it happens
Trigger: A cluster Step/Submit request arrives over a connection that did not present a TLS client certificate — TLS client auth disabled or misconfigured, or the request was not routed through the mutual-TLS gRPC server.
Common situations: Orderer TLS enabled but clientAuthRequired not set (or vice versa) in orderer.yaml; a test/tool connecting with one-way TLS; a proxy/LB terminating TLS and stripping client certs; sender built without TLS credentials.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- client didn't include its TLS cert hash
- client didn't send a TLS certificate
- claimed TLS cert hash is %v but actual TLS cert hash is %v
- peer.tls.clientKey.file and peer.tls.clientCert.file must bo
- peer.tls.key.file and peer.tls.cert.file must both be set or
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/9374600222ef6f34.
Report an issue: GitHub.