temporalio/temporal · error

failed to load CA files: %v

Error message

failed to load CA files: %v

What it means

registerTLSConfig in the MySQL session package reads the configured CA PEM file (cfg.TLS.CaFile) to build the root certificate pool for the connection; if os.ReadFile fails, the error is wrapped as "failed to load CA files". The MySQL client cannot establish verified TLS without this file.

Source

Thrown at common/persistence/sql/sqlplugin/mysql/session/session.go:216

		return key, value
	default:
		return inkey, invalue
	}
}

func registerTLSConfig(cfg *config.SQL) error {
	if cfg.TLS == nil || !cfg.TLS.Enabled {
		return nil
	}

	// TODO: create a way to set MinVersion and CipherSuites via cfg.
	tlsConfig := auth.NewTLSConfigForServer(cfg.TLS.ServerName, cfg.TLS.EnableHostVerification)

	if cfg.TLS.CaFile != "" {
		rootCertPool := x509.NewCertPool()
		pem, err := os.ReadFile(cfg.TLS.CaFile)
		if err != nil {
			return fmt.Errorf("failed to load CA files: %v", err)
		}
		if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
			return fmt.Errorf("failed to append CA file")
		}
		tlsConfig.RootCAs = rootCertPool
	}

	if cfg.TLS.CertFile != "" && cfg.TLS.KeyFile != "" {
		clientCert := make([]tls.Certificate, 0, 1)
		certs, err := tls.LoadX509KeyPair(
			cfg.TLS.CertFile,
			cfg.TLS.KeyFile,
		)
		if err != nil {
			return fmt.Errorf("failed to load tls x509 key pair: %v", err)
		}
		clientCert = append(clientCert, certs)
		tlsConfig.Certificates = clientCert

View on GitHub (pinned to bde624efd1)

Solutions

  1. Verify the path in TLS.CaFile exists and is readable by the Temporal process user (ls -l / check permissions).
  2. Use an absolute path in containerized deployments and ensure the file is mounted (e.g., via a Kubernetes secret volume).
  3. Fix ownership/permissions (chmod/chown) so the service account can read the PEM file.
  4. If TLS verification is not required for the environment, remove the CaFile setting — but prefer fixing the mount for security.

Example fix

// before (config)
tls:
  caFile: certs/ca.pem
// after
tls:
  caFile: /etc/temporal/certs/ca.pem  # absolute path, verified mounted & readable
Defensive patterns

Strategy: validation

Validate before calling

caPath := cfg.TLS.CaFile
if fi, err := os.Stat(caPath); err != nil || fi.Size() == 0 {
    return fmt.Errorf("CA file %q missing or empty", caPath)
}
if f, err := os.Open(caPath); err != nil {
    return fmt.Errorf("CA file %q not readable: %v", caPath, err)
} else { f.Close() }

Try / catch

if err := connectDB(cfg); err != nil {
    if strings.Contains(err.Error(), "failed to load CA files") {
        // fix caFile path/mount/permissions before reconnect
    }
    return err
}

Prevention

When it happens

Trigger: createConnection -> registerTLSConfig with a non-empty TLS.CaFile that points to a nonexistent, unreadable, or otherwise un-openable file path.

Common situations: Typo in the CA file path in config; file not mounted into a container (missing volume mount); permission denied for the service user; relative path used with a different working directory in a container.

Related errors


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