temporalio/temporal · error

failed to append CA file

Error message

failed to append CA file

What it means

After the CA file is read successfully, registerTLSConfig parses it with x509.AppendCertsFromPEM; if no certificates could be parsed from the bytes, it returns "failed to append CA file". This means the file exists but does not contain any valid PEM-encoded certificates.

Solutions

  1. Verify the file contains PEM blocks (-----BEGIN CERTIFICATE-----) with: openssl x509 -in ca.pem -text -noout.
  2. Convert DER certificates to PEM: openssl x509 -inform der -in ca.crt -out ca.pem.
  3. Re-create/re-mount the secret and confirm the file is non-empty and uncorrupted inside the container.
  4. Concatenate the full CA chain (intermediates + root) into the file if the server uses an intermediate chain.

Example fix

// before: ca.pem contains a DER binary certificate
// convert it
openssl x509 -inform der -in ca.crt -out /etc/temporal/certs/ca.pem
// after: ca.pem starts with
// -----BEGIN CERTIFICATE-----
Defensive patterns

Strategy: validation

Validate before calling

pemBytes, err := os.ReadFile(cfg.TLS.CaFile)
if err != nil { return err }
if !x509.NewCertPool().AppendCertsFromPEM(pemBytes) {
    return fmt.Errorf("file %q contains no valid PEM certificates", cfg.TLS.CaFile)
}

Type guard

func isValidPEMCA(path string) bool {
    b, err := os.ReadFile(path)
    if err != nil { return false }
    return x509.NewCertPool().AppendCertsFromPEM(b)
}

Try / catch

if err := connectDB(cfg); err != nil {
    if strings.Contains(err.Error(), "failed to append CA file") {
        // convert cert to PEM (openssl x509 -inform der ...) and retest
    }
    return err
}

Prevention

When it happens

Trigger: createConnection -> registerTLSConfig where TLS.CaFile points to an existing file whose contents are not parseable PEM certificates (empty file, wrong format, DER-encoded cert, concatenated junk).

Common situations: Secret mounted as an empty/placeholder file; DER (.crt binary) cert provided instead of PEM; file containing a private key or chain without the CA cert; truncation during secret provisioning; YAML inlining mangling the PEM.

Related errors


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

Appendix: source

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

	}
}

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
	}

	// In order to use the TLS configuration you need to register it. Once registered you use it by specifying

View on GitHub (pinned to bde624efd1)