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.

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)

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.