temporalio/temporal · critical

failed to fetch client CAs: %v

Error message

failed to fetch client CAs: %v

What it means

When mutual TLS is enabled (requireClientAuth), the server fetches the CA pool used to verify client certificates via certProvider.FetchClientCAs(). Any failure is wrapped as "failed to fetch client CAs: %v". This blocks construction of the server TLS config, since client certificate verification requires a trust pool.

Source

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

		return nil, fmt.Errorf("loading server tls certificate failed: %v", err)
	}

	// tls disabled, responsibility of cert provider above to error otherwise
	if serverCert == nil {
		return nil, nil
	}

	// Default to NoClientAuth
	clientAuthType := tls.NoClientCert
	var clientCaPool *x509.CertPool

	// If mTLS enabled
	if requireClientAuth {
		clientAuthType = tls.RequireAndVerifyClientCert

		ca, err := certProvider.FetchClientCAs()
		if err != nil {
			return nil, fmt.Errorf("failed to fetch client CAs: %v", err)
		}

		clientCaPool = ca
	}
	if remoteAddress != "" { // remoteAddress=="" when we return initial tls.Config object when configuring server
		logger.Debug("returning TLS config for connection", tag.Address(remoteAddress), tag.ServerName(serverName))
	}
	return auth.NewTLSConfigWithCertsAndCAs(
		clientAuthType,
		[]tls.Certificate{*serverCert},
		clientCaPool,
		logger), nil
}

func newClientTLSConfig(
	clientProvider CertProvider,
	serverName string,
	isAuthRequired bool,

View on GitHub (pinned to bde624efd1)

Solutions

  1. Check the wrapped error for the root cause (path, permissions, PEM decode) and fix the client CA source.
  2. Ensure clientCADataFile (or inline clientCAData) contains a valid PEM CA bundle.
  3. Confirm the CA bundle includes the CA that actually signed your client certificates.
  4. If mTLS is not needed, set requireClientAuth=false so the CA fetch is skipped.

Example fix

// before
serverTLS:
  requireClientAuth: true   # clientCADataFile missing
// after
serverTLS:
  requireClientAuth: true
  clientCADataFile: /etc/temporal/tls/client-ca.pem
Defensive patterns

Strategy: validation

Validate before calling

caPEM, err := os.ReadFile(cfg.ClientCADataFile)
if err != nil { return fmt.Errorf("client CA unreadable: %w", err) }
if !hasPEMCert(caPEM) { return errors.New("client CA bundle has no certificates") }

Try / catch

cfg, err := getServerTLSConfigFromCertProvider(...)
if err != nil && strings.Contains(err.Error(), "failed to fetch client CAs") {
    // disable mTLS only if policy allows; otherwise fail fast
    logger.Fatal("client CA pool unavailable for mTLS", "cause", err)
}

Prevention

When it happens

Trigger: Server TLS config built with requireClientAuth=true and the provider's client-CA file/data is missing, unreadable, or contains no parseable certificates (propagating errors like 390 from parseCert/buildCAPool).

Common situations: mTLS enabled but ClientCA (clientCADataFile / clientCAData) not configured or pointing to a wrong path; CA bundle file empty after secret rotation; clients authenticate with certs issued by a CA whose bundle failed to load.

Related errors


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