temporalio/temporal · error · ErrTLSConfig

only one of keyData or keyFile properties should be specifie

Error message

only one of keyData or keyFile properties should be specified

What it means

This error is returned by validateTemporalTls when the private key is provided both inline (KeyData) and via a file path (KeyFile). The library refuses to guess which key to use and wraps the failure with ErrTLSConfig so misconfigured TLS blocks fail fast at config load.

Source

Thrown at common/auth/tls_config_helper.go:125

	// Load client cert
	clientCert, err := parseClientCert(temporalTls)
	if err != nil {
		return nil, err
	}
	if clientCert != nil {
		tlsConfig.Certificates = []tls.Certificate{*clientCert}
	}

	return tlsConfig, nil
}

func validateTemporalTls(temporalTls *TLS) error {
	if temporalTls.CertData != "" && temporalTls.CertFile != "" {
		return fmt.Errorf("%w: %s", ErrTLSConfig, "only one of certData or certFile properties should be specified")
	}

	if temporalTls.KeyData != "" && temporalTls.KeyFile != "" {
		return fmt.Errorf("%w: %s", ErrTLSConfig, "only one of keyData or keyFile properties should be specified")
	}

	certProvided := temporalTls.CertData != "" || temporalTls.CertFile != ""
	keyProvided := temporalTls.KeyData != "" || temporalTls.KeyFile != ""
	if certProvided != keyProvided {
		return fmt.Errorf("%w: %s", ErrTLSConfig, "cert or key is missing")
	}

	if temporalTls.CaData != "" && temporalTls.CaFile != "" {
		return fmt.Errorf("%w: %s", ErrTLSConfig, "only one of caData or caFile properties should be specified")
	}
	return nil
}

func parseCAs(temporalTls *TLS) (*x509.CertPool, error) {
	var caBytes []byte
	var err error
	if temporalTls.CaFile != "" {

View on GitHub (pinned to bde624efd1)

Solutions

  1. Keep exactly one: delete KeyData if the key is on disk, or delete KeyFile if using inline base64.
  2. Check config merge/override precedence and unset the redundant field.
  3. Add a startup config test that fails when both key sources are set.

Example fix

// before
tls:
  keyData: "LS0tLS1CRUdJTi..."
  keyFile: "/etc/temporal/certs/client.key"
// after
tls:
  keyFile: "/etc/temporal/certs/client.key"
Defensive patterns

Strategy: validation

Validate before calling

func validateTLSOneKeySource(t *auth.TLS) error {
	if t.KeyData != "" && t.KeyFile != "" {
		return fmt.Errorf("set only one of keyData or keyFile")
	}
	return nil
}

Type guard

func hasExactlyOne(a, b string) bool { return (a != "") != (b != "") }

Prevention

When it happens

Trigger: Calling NewTLSConfig with a *TLS struct where both KeyData != "" and KeyFile != "" — e.g. a config file that sets both, or merged config layers each contributing a key source.

Common situations: Environment-specific overrides adding a key file while an inline base64 key remains; secrets tooling injecting keyData on top of a keyFile; copy-paste from two different example configs.

Related errors


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