temporalio/temporal · error · ErrTLSConfig
unable to decode client ca data
Error message
unable to decode client ca data
What it means
parseCAs returns this error when CaData is set but base64.StdEncoding.DecodeString fails, meaning the value is not valid base64 (invalid characters, wrong padding, or a raw PEM pasted without encoding). The decode error is chained and wrapped with ErrTLSConfig.
Source
Thrown at common/auth/tls_config_helper.go:151
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 != "" {
caBytes, err = os.ReadFile(temporalTls.CaFile)
if err != nil {
return nil, fmt.Errorf("%w: %s (%w)", ErrTLSConfig, "unable to read client ca file", err)
}
} else if temporalTls.CaData != "" {
caBytes, err = base64.StdEncoding.DecodeString(temporalTls.CaData)
if err != nil {
return nil, fmt.Errorf("%w: %s (%w)", ErrTLSConfig, "unable to decode client ca data", err)
}
}
if len(caBytes) > 0 {
caCertPool := x509.NewCertPool()
caCerts, err := parseCertsFromPEM(caBytes)
if len(caCerts) == 0 {
return nil, fmt.Errorf("%w: %s (%w)", ErrTLSConfig, "unable to parse certs as PEM", err)
}
for _, cert := range caCerts {
caCertPool.AddCert(cert)
}
if err != nil {
return nil, fmt.Errorf("%w: %s (%w)", ErrTLSConfig, "unable to load decoded CA Cert as PEM", err)
}
return caCertPool, nil
}
return nil, nil
}View on GitHub (pinned to bde624efd1)
Solutions
- Re-encode the CA PEM with: base64 -w0 ca.pem (single-line, no newlines) and use that exact string for CaData.
- Confirm you are not double-encoding: the value must decode once to PEM text beginning with -----BEGIN CERTIFICATE-----.
- Strip surrounding whitespace/quotes or YAML multiline issues that corrupt the base64 string.
- Alternatively switch to CaFile pointing at the PEM on disk to avoid encoding entirely.
Example fix
// before tls: caData: "-----BEGIN CERTIFICATE-----\nMIID..." # raw PEM, not base64 // after tls: caData: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQ..." # base64 -w0 ca.pem
Defensive patterns
Strategy: validation
Validate before calling
func checkCaDataIsBase64(caData string) error {
decoded, err := base64.StdEncoding.DecodeString(caData)
if err != nil {
return fmt.Errorf("caData is not valid base64: %w", err)
}
if !strings.Contains(string(decoded), "BEGIN CERTIFICATE") {
return fmt.Errorf("caData does not decode to PEM certificate")
}
return nil
} Prevention
- Generate inline values only via base64 -w0 file.pem (single line, no wrapping)
- Never paste raw PEM into *Data fields
- If your config format inserts newlines, use caFile instead of caData
- Verify decoded content locally before deploy: echo '<value>' | base64 -d | openssl x509 -noout -subject
When it happens
Trigger: NewTLSConfig -> parseCAs with CaData containing non-base64 text — e.g. a PEM block pasted verbatim, a base64 string with newlines/whitespace handled incorrectly, or truncated data.
Common situations: Pasting certificate content directly into YAML instead of base64-encoding it; editors/tools adding line breaks that break decoding depending on how config is parsed; double-encoding (base64 of base64) leaving stray characters; quoting/escaping issues in templated config.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- unable to decode client certificate
- only one of certData or certFile properties should be specif
- only one of certData or certFile properties should be specif
- only one of keyData or keyFile properties should be specifie
- cert or key is missing
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/5aadad62d64dce01.
Report an issue: GitHub.