hyperledger/fabric · error

chaincode tls key not provided

Error message

chaincode tls key not provided

What it means

When TLS is required and client authentication (mutual TLS) is also required, the peer needs the chaincode's client TLS private key to present in the handshake; connection.json without `client_key` cannot form a mutual-TLS dial.

Source

Thrown at core/container/externalbuilder/instance.go:95

func (c *ChaincodeServerUserData) ChaincodeServerInfo(cryptoDir string) (*ccintf.ChaincodeServerInfo, error) {
	if c.Address == "" {
		return nil, errors.New("chaincode address not provided")
	}
	connInfo := &ccintf.ChaincodeServerInfo{Address: c.Address}

	connInfo.ClientConfig.DialTimeout = time.Duration(c.DialTimeout)
	if connInfo.ClientConfig.DialTimeout == 0 {
		connInfo.ClientConfig.DialTimeout = DialTimeout
	}

	// we can expose this if necessary
	connInfo.ClientConfig.KaOpts = comm.DefaultKeepaliveOptions

	if !c.TLSRequired {
		return connInfo, nil
	}
	if c.ClientAuthRequired && c.ClientKey == "" {
		return nil, errors.New("chaincode tls key not provided")
	}
	if c.ClientAuthRequired && c.ClientCert == "" {
		return nil, errors.New("chaincode tls cert not provided")
	}
	if c.RootCert == "" {
		return nil, errors.New("chaincode tls root cert not provided")
	}

	connInfo.ClientConfig.SecOpts.UseTLS = true

	if c.ClientAuthRequired {
		connInfo.ClientConfig.SecOpts.RequireClientCert = true
		connInfo.ClientConfig.SecOpts.Certificate = []byte(c.ClientCert)
		connInfo.ClientConfig.SecOpts.Key = []byte(c.ClientKey)
		connInfo.ClientConfig.SecOpts.ServerNameOverride = c.Domain
	}

	connInfo.ClientConfig.SecOpts.ServerRootCAs = [][]byte{[]byte(c.RootCert)}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Set `client_key` in connection.json to the PEM-encoded client TLS private key
  2. If mutual TLS is not needed, set client_auth_required to false
  3. Ensure the release script/template includes all four TLS fields (client_key, client_cert, root_cert, address)

Example fix

// before
{"tls_required":true,"client_auth_required":true,"client_cert":"cert.pem"}
// after
{"tls_required":true,"client_auth_required":true,"client_cert":"cert.pem","client_key":"key.pem","root_cert":"root.pem"}
Defensive patterns

Strategy: validation

Validate before calling

var ud ChaincodeServerUserData
json.Unmarshal(connJSON, &ud)
if ud.TLSRequired && ud.ClientAuthRequired && ud.ClientKey == "" {
    return errors.New("client_key PEM required for mutual TLS")
}

Try / catch

info, err := ud.ChaincodeServerInfo(cryptoDir)
if err != nil && strings.Contains(err.Error(), "tls key not provided") {
    return fmt.Errorf("provide client_key in connection.json or disable client_auth_required: %w", err)
}

Prevention

When it happens

Trigger: connection.json has tls_required and client_auth_required true, but `client_key` is empty when ChaincodeServerInfo assembles the client SecOpts.

Common situations: Release step emitted certs but not the key; key mounted but path/env not passed into connection.json; user intentionally omitted private key for security but enabled client auth.

Understand the failure class

Related errors


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