hyperledger/fabric · error
TLS is active but chaincode
Error message
TLS is active but chaincode %s didn't send certificate
What it means
When mutual TLS is enabled between peer and chaincode, the Authenticator requires the chaincode's TLS client certificate during registration. If the stream's gRPC context carries no certificate hash (the client presented no certificate, or TLS metadata is absent), registration is rejected with 'TLS is active but chaincode %s didn't send certificate'.
Solutions
- Configure the chaincode to use TLS with its client certificate (set CORE_CHAINCODE_TLS_CERT / CORE_CHAINCODE_TLS_KEY to the cert/key files).
- Ensure the chaincode's connection address uses the TLS port and the peer's chaincode TLS settings match (peer.chaincode.cert.enabled).
- If running in a container, verify the TLS files are mounted and readable, and the chaincode connects over TLS not plaintext.
- Remove any TLS-terminating proxy between chaincode and peer, or re-sign at the peer side so client certs reach the peer.
Example fix
// before: chaincode started without TLS CORE_CHAINCODE_ID_NAME=mycc:1.0 /bin/chaincode -peer.address peer:7052 // after: chaincode started with TLS certs CORE_CHAINCODE_ID_NAME=mycc:1.0 CORE_CHAINCODE_TLS_CERT=/certs/tls.crt CORE_CHAINCODE_TLS_KEY=/certs/tls.key /bin/chaincode -peer.address peer:7052
Defensive patterns
Strategy: validation
Validate before calling
// before connecting, confirm TLS material is present
if _, err := tls.LoadX509KeyPair(tlsCertPath, tlsKeyPath); err != nil {
return fmt.Errorf("chaincode TLS cert/key missing: %w", err)
} Type guard
func tlsCredentialsPresent(certPath, keyPath string) bool {
_, err := tls.LoadX509KeyPair(certPath, keyPath)
return err == nil
} Try / catch
if err := authenticate(msg, stream); err != nil {
if strings.Contains(err.Error(), "didn't send certificate") {
// re-dial the peer with tls.Credentials{Certificates: clientCerts}
}
} Prevention
- Match chaincode TLS settings to the peer's chaincode.cert.enabled configuration
- Mount TLS cert/key files into the chaincode container and point CORE_CHAINCODE_TLS_CERT/KEY at them
- Avoid TLS-terminating proxies between chaincode and peer
- Use the TLS-enabled peer address when mtls is on
When it happens
Trigger: A chaincode registers over a connection where extractCertificateHashFromContext finds no client certificate: TLS client auth not configured on the chaincode's connection, or the chaincode's side of the gRPC connection was established without TLS.
Common situations: Chaincode configured with TLS disabled while the peer runs with peer.chaincode.cert.enabled=true (mtls); a dev-mode chaincode or local runner not presenting its TLS keypair; a proxy/load balancer terminating TLS so the peer never sees the client cert.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Chaincode with given certificate hash belongs to a…
- Chaincode with given certificate hash not found in registry
- First message needs to be a register
- access denied
- cannot collect files from empty chaincode path
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/947c5dda7e4a2a18.
Report an issue: GitHub.
Appendix: source
Thrown at core/chaincode/accesscontrol/access.go:78
func (ac *Authenticator) authenticate(msg *pb.ChaincodeMessage, stream grpc.ServerStream) error {
if msg.Type != pb.ChaincodeMessage_REGISTER {
logger.Warning("Got message", msg, "but expected a ChaincodeMessage_REGISTER message")
return errors.New("First message needs to be a register")
}
chaincodeID := &pb.ChaincodeID{}
err := proto.Unmarshal(msg.Payload, chaincodeID)
if err != nil {
logger.Warning("Failed unmarshalling message:", err)
return err
}
ccName := chaincodeID.Name
// Obtain certificate from stream
hash := extractCertificateHashFromContext(stream.Context())
if len(hash) == 0 {
errMsg := fmt.Sprintf("TLS is active but chaincode %s didn't send certificate", ccName)
logger.Warning(errMsg)
return errors.New(errMsg)
}
// Look it up in the mapper
registeredName := ac.mapper.lookup(certHash(hash))
if registeredName == "" {
errMsg := fmt.Sprintf("Chaincode %s with given certificate hash %v not found in registry", ccName, hash)
logger.Warning(errMsg)
return errors.New(errMsg)
}
if registeredName != ccName {
errMsg := fmt.Sprintf("Chaincode %s with given certificate hash %v belongs to a different chaincode", ccName, hash)
logger.Warning(errMsg)
return errors.New(errMsg)
}
logger.Debug("Chaincode", ccName, "'s authentication is authorized")
return nil
}
View on GitHub (pinned to 2736b63f8f)