temporalio/temporal · error

client auth required, but no certificate provided

Error message

client auth required, but no certificate provided

What it means

In newClientTLSConfig, when client auth (mTLS) is required, the fetcher calls clientProvider.FetchClientCertificate(isWorker). If the provider returns success but a nil certificate — meaning it is configured with no client cert available — this explicit error is raised. It prevents silently starting mTLS handshakes with no client identity.

Source

Thrown at common/rpc/encryption/local_store_tls_provider.go:390

) (*tls.Config, error) {
	// Optional ServerCA for client if not already trusted by host
	serverCa, err := clientProvider.FetchServerRootCAsForClient(isWorker)
	if err != nil {
		return nil, fmt.Errorf("failed to load client ca: %v", err)
	}

	var getCert tlsCertFetcher

	// mTLS enabled, present certificate
	if isAuthRequired {
		getCert = func() (*tls.Certificate, error) {
			cert, err := clientProvider.FetchClientCertificate(isWorker)
			if err != nil {
				return nil, err
			}

			if cert == nil {
				return nil, fmt.Errorf("client auth required, but no certificate provided")
			}
			return cert, nil
		}
	}

	return auth.NewDynamicTLSClientConfig(
		getCert,
		serverCa,
		serverName,
		enableHostVerification,
	), nil
}

func (s *localStoreTlsProvider) timerCallback() {
	for {
		select {
		case <-s.stop:
			return

View on GitHub (pinned to bde624efd1)

Solutions

  1. Configure client cert/key data (certDataFile/certData and keyDataFile/keyData) for the relevant client group (internode or frontend).
  2. Obtain a client certificate issued by the server's client CA and mount it for the process.
  3. If mTLS is not intended, disable requireClientAuth on the server so clients are not asked for certificates.
  4. Verify the cert file is populated (not an empty secret) and matches the key.

Example fix

// before
clientTLS:
  internode:
    serverName: tls-server   # no client cert configured
// after
clientTLS:
  internode:
    serverName: tls-server
    certDataFile: /etc/temporal/tls/client.pem
    keyDataFile: /etc/temporal/tls/client-key.pem
Defensive patterns

Strategy: validation

Validate before calling

if isAuthRequired {
    certPEM, err := os.ReadFile(cfg.CertDataFile)
    if err != nil { return fmt.Errorf("client cert unreadable: %w", err) }
    keyPEM, err := os.ReadFile(cfg.KeyDataFile)
    if err != nil { return fmt.Errorf("client key unreadable: %w", err) }
    if _, err := tls.X509KeyPair(certPEM, keyPEM); err != nil { return fmt.Errorf("invalid client keypair: %w", err) }
}

Try / catch

tlsCfg, err := newClientTLSConfig(...)
if err != nil && strings.Contains(err.Error(), "client auth required, but no certificate provided") {
    return fmt.Errorf("mTLS requested but client identity not configured: %w", err)
}

Prevention

When it happens

Trigger: isAuthRequired=true (mutual TLS) but the localStore provider has no client certificate path/data configured, or the configured cert is absent so the fetcher returns (nil, nil).

Common situations: Server demands client certs but the client config only sets serverCA and serverName, omitting the client cert/key pair; cert file removed by rotation without config update; worker enabled for mTLS on the server side but client-side cert block left empty.

Understand the failure class

Related errors


AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01). Data as JSON: /api/errors/a3f6ddf3c5c1e464. Report an issue: GitHub.