hyperledger/fabric · error
client didn't send a TLS certificate
Error message
client didn't send a TLS certificate
What it means
mutualTLSBinding extracts the actual TLS certificate hash from the gRPC context (util.ExtractCertificateHashFromContext). An empty result means no TLS certificate is present in the connection, so binding cannot be verified.
Source
Thrown at common/deliver/binding.go:56
return errors.New("message is nil")
}
return inspectMessage(ctx, extractTLSCertHash(msg))
}
}
// mutualTLSBinding enforces the client to send its TLS cert hash in the message,
// and then compares it to the computed hash that is derived
// from the gRPC context.
// In case they don't match, or the cert hash is missing from the request or
// there is no TLS certificate to be excavated from the gRPC context,
// an error is returned.
func mutualTLSBinding(ctx context.Context, claimedTLScertHash []byte) error {
if len(claimedTLScertHash) == 0 {
return errors.Errorf("client didn't include its TLS cert hash")
}
actualTLScertHash := util.ExtractCertificateHashFromContext(ctx)
if len(actualTLScertHash) == 0 {
return errors.Errorf("client didn't send a TLS certificate")
}
if !bytes.Equal(actualTLScertHash, claimedTLScertHash) {
return errors.Errorf("claimed TLS cert hash is %v but actual TLS cert hash is %v", claimedTLScertHash, actualTLScertHash)
}
return nil
}
// noopBinding is a BindingInspector that always returns nil
func noopBinding(_ context.Context, _ []byte) error {
return nil
}
View on GitHub (pinned to 2736b63f8f)
Solutions
- Configure the client with a client keypair (keyFile/certFile) for mutual TLS
- Ensure the server has client auth required (ClientAuth: tls.RequireAndVerifyClientCert) and no proxy strips the cert
- Verify the client actually connects via https/grpcs, not plain http/grpc
Example fix
// before
conn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
// after
tlsCfg := &tls.Config{Certificates: []tls.Certificate{clientCert}}
conn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(credentials.NewTLS(tlsCfg))) Defensive patterns
Strategy: validation
Validate before calling
func hasClientTLSContext(ctx context.Context) bool {
_, ok := credentials.FromContext(ctx).AuthType(), false
_ = ok
return util.ExtractCertificateHashFromContext(ctx) != nil
} Try / catch
if err := inspector(ctx, msg); err != nil {
if strings.Contains(err.Error(), "didn't send a TLS certificate") {
return status.Error(codes.Unauthenticated, "client must connect with mutual TLS")
}
return err
} Prevention
- Require client certificates on the server (RequireAndVerifyClientCert)
- Always configure client keypair when connecting to binding-enforced services
- Check proxy/TLS-terminator config so client certs reach the backend
When it happens
Trigger: Request arrives on a connection without a client TLS certificate (non-TLS connection, or TLS without client auth) while mutual TLS binding is enforced.
Common situations: Client connecting over plaintext to an orderer expecting TLS; client TLS config missing CertFile/KeyFile (no mutual TLS); a proxy/LB terminating TLS and dropping client certs.
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
- claimed TLS cert hash is %v but actual TLS cert hash is %v
- no TLS certificate sent
- message is nil
- chaincode tls key not provided
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/0c500fe8ff3ebafb.
Report an issue: GitHub.