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
- Keep exactly one: delete KeyData if the key is on disk, or delete KeyFile if using inline base64.
- Check config merge/override precedence and unset the redundant field.
- 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
- Choose one key delivery mechanism (file mount or secret inline) per deployment and stick to it
- Validate merged config in CI before deploy
- When injecting keys via secrets tooling, clear the other field in the same change
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
- only one of certData or certFile properties should be specif
- cert or key is missing
- only one of caData or caFile properties should be specified
- invalid value for publicClient.forceTLSConfig: %q
- host name cannot be empty string
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/732a1fc67e74be63.
Report an issue: GitHub.