hyperledger/fabric · error

peer.tls.clientKey.file and peer.tls.clientCert.file must bo

Error message

peer.tls.clientKey.file and peer.tls.clientCert.file must both be set or must both be empty

What it means

GetClientCertificate resolves the client key/cert pair used for mutual TLS. If exactly one of peer.tls.clientKey.file and peer.tls.clientCert.file is set (XOR condition), the configuration is invalid and this error is returned; both must be set together or both left empty.

Source

Thrown at core/peer/config.go:469

	}
	if viper.IsSet("peer.maxSendMsgSize") {
		serverConfig.MaxSendMsgSize = int(viper.GetInt32("peer.maxSendMsgSize"))
	}
	return serverConfig, nil
}

// GetClientCertificate returns the TLS certificate to use for gRPC client
// connections
func GetClientCertificate() (tls.Certificate, error) {
	cert := tls.Certificate{}

	keyPath := viper.GetString("peer.tls.clientKey.file")
	certPath := viper.GetString("peer.tls.clientCert.file")

	if keyPath != "" || certPath != "" {
		// need both keyPath and certPath to be set
		if keyPath == "" || certPath == "" {
			return cert, errors.New("peer.tls.clientKey.file and " +
				"peer.tls.clientCert.file must both be set or must both be empty")
		}
		keyPath = config.GetPath("peer.tls.clientKey.file")
		certPath = config.GetPath("peer.tls.clientCert.file")

	} else {
		// use the TLS server keypair
		keyPath = viper.GetString("peer.tls.key.file")
		certPath = viper.GetString("peer.tls.cert.file")

		if keyPath != "" || certPath != "" {
			// need both keyPath and certPath to be set
			if keyPath == "" || certPath == "" {
				return cert, errors.New("peer.tls.key.file and " +
					"peer.tls.cert.file must both be set or must both be empty")
			}
			keyPath = config.GetPath("peer.tls.key.file")
			certPath = config.GetPath("peer.tls.cert.file")

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Set both CORE_PEER_TLS_CLIENTKEY_FILE and CORE_PEER_TLS_CLIENTCERT_FILE (and the corresponding core.yaml keys) to valid paths.
  2. Or unset/remove both keys if client TLS keypair is not needed.
  3. Verify both files exist and are non-empty at the configured paths.
  4. Check docker-compose/K8s env blocks so the pair is always set together.

Example fix

// before (environment)
CORE_PEER_TLS_CLIENTKEY_FILE=/etc/hyperledger/fabric/tls/client.key
// (clientCert missing)
// after
CORE_PEER_TLS_CLIENTKEY_FILE=/etc/hyperledger/fabric/tls/client.key
CORE_PEER_TLS_CLIENTCERT_FILE=/etc/hyperledger/fabric/tls/client.crt
Defensive patterns

Strategy: validation

Validate before calling

keyPath := viper.GetString("peer.tls.clientKey.file")
certPath := viper.GetString("peer.tls.clientCert.file")
if (keyPath == "") != (certPath == "") {
  return errors.New("client key and client cert must both be set or both empty")
}

Try / catch

cert, err := GetClientCertificate()
if err != nil && strings.Contains(err.Error(), "clientCert.file must both be set") {
  log.Fatalf("set both client key and cert TLS files: %v", err)
}

Prevention

When it happens

Trigger: Calling GetClientCertificate when only one of peer.tls.clientKey.file / peer.tls.clientCert.file is non-empty in viper config (core.yaml or CORE_PEER_TLS_CLIENTKEY_FILE / CORE_PEER_TLS_CLIENTCERT_FILE env vars).

Common situations: Setting CORE_PEER_TLS_CLIENTKEY_FILE but forgetting CORE_PEER_TLS_CLIENTCERT_FILE (or vice versa) in docker-compose env; partial secret mount containing only one of the two files; copy-paste config from a non-TLS peer.

Understand the failure class

Related errors


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