{"record":{"id":"732a1fc67e74be63","repo":"temporalio/temporal","slug":"only-one-of-keydata-or-keyfile-properties-should-b-732a1f","errorCode":null,"errorMessage":"only one of keyData or keyFile properties should be specified","messagePattern":"only one of keyData or keyFile properties should be specified","errorType":"validation","errorClass":"ErrTLSConfig","httpStatus":null,"severity":"error","filePath":"common/auth/tls_config_helper.go","lineNumber":125,"sourceCode":"\t// Load client cert\n\tclientCert, err := parseClientCert(temporalTls)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif clientCert != nil {\n\t\ttlsConfig.Certificates = []tls.Certificate{*clientCert}\n\t}\n\n\treturn tlsConfig, nil\n}\n\nfunc validateTemporalTls(temporalTls *TLS) error {\n\tif temporalTls.CertData != \"\" && temporalTls.CertFile != \"\" {\n\t\treturn fmt.Errorf(\"%w: %s\", ErrTLSConfig, \"only one of certData or certFile properties should be specified\")\n\t}\n\n\tif temporalTls.KeyData != \"\" && temporalTls.KeyFile != \"\" {\n\t\treturn fmt.Errorf(\"%w: %s\", ErrTLSConfig, \"only one of keyData or keyFile properties should be specified\")\n\t}\n\n\tcertProvided := temporalTls.CertData != \"\" || temporalTls.CertFile != \"\"\n\tkeyProvided := temporalTls.KeyData != \"\" || temporalTls.KeyFile != \"\"\n\tif certProvided != keyProvided {\n\t\treturn fmt.Errorf(\"%w: %s\", ErrTLSConfig, \"cert or key is missing\")\n\t}\n\n\tif temporalTls.CaData != \"\" && temporalTls.CaFile != \"\" {\n\t\treturn fmt.Errorf(\"%w: %s\", ErrTLSConfig, \"only one of caData or caFile properties should be specified\")\n\t}\n\treturn nil\n}\n\nfunc parseCAs(temporalTls *TLS) (*x509.CertPool, error) {\n\tvar caBytes []byte\n\tvar err error\n\tif temporalTls.CaFile != \"\" {","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/auth/tls_config_helper.go#L107-L143","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ntls:\n  keyData: \"LS0tLS1CRUdJTi...\"\n  keyFile: \"/etc/temporal/certs/client.key\"\n// after\ntls:\n  keyFile: \"/etc/temporal/certs/client.key\"","handlingStrategy":"validation","validationCode":"func validateTLSOneKeySource(t *auth.TLS) error {\n\tif t.KeyData != \"\" && t.KeyFile != \"\" {\n\t\treturn fmt.Errorf(\"set only one of keyData or keyFile\")\n\t}\n\treturn nil\n}","typeGuard":"func hasExactlyOne(a, b string) bool { return (a != \"\") != (b != \"\") }","tryCatchPattern":null,"preventionTips":["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"],"tags":["tls","config","validation"],"backgroundTag":"tls-config-conflict","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}