temporalio/temporal · error · ErrTLSConfig
unable to load decoded CA Cert as PEM
Error message
unable to load decoded CA Cert as PEM
What it means
This is the check in parseCAs that reports any error from parseCertsFromPEM after the certificate list was non-empty. In practice it is nearly unreachable: the zero-certificate case is already handled by the 'unable to parse certs as PEM' branch, so this only fires if parsing both returned certs and errored simultaneously. It is wrapped with ErrTLSConfig like its siblings.
Source
Thrown at common/auth/tls_config_helper.go:164
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
}
func parseCertsFromPEM(pemCerts []byte) ([]*x509.Certificate, error) {
for len(pemCerts) > 0 {
var block *pem.Block
block, pemCerts = pem.Decode(pemCerts)
if block == nil {
break
}
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
continue
}
certBytes := block.BytesView on GitHub (pinned to bde624efd1)
Solutions
- Inspect the chained error and validate the CA bundle with openssl x509 / openssl verify to find malformed blocks.
- Split a large CA bundle into individual PEM certs and add them back one at a time to isolate the bad block.
- Regenerate or re-download the CA bundle from the source authority.
Example fix
// Split and validate the bundle externally // openssl x509 -in ca-bundle.pem -noout -subject // before: one possibly malformed bundle file // after: only verified PEM certs concatenated: // cat ca1.pem ca2.pem > /etc/temporal/certs/ca.pem
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-validate the CA bundle parses cleanly and completely
func checkBundle(caBytes []byte) error {
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(caBytes) {
return fmt.Errorf("bundle contains no parseable certs")
}
return nil
} Try / catch
pool, err := parseCAs(cfg) // via NewTLSConfig
if err != nil {
if errors.Is(err, auth.ErrTLSConfig) {
logger.Error("CA bundle rejected", tag.Key, err)
}
return err
} Prevention
- Split large CA bundles into individually verified PEM files before concatenating
- Regenerate bundles from the issuing authority rather than hand-editing
- Test each cert in a bundle with openssl x509 before combining
When it happens
Trigger: NewTLSConfig -> parseCAs where parseCertsFromPEM returns a non-empty cert list AND a non-nil error — an edge case during PEM parsing of partially malformed data.
Common situations: Rarely seen by developers; may surface with unusual CA bundles containing both valid certs and malformed blocks, depending on parseCertsFromPEM behavior.
Related errors
- unable to parse certs as PEM
- failed to load decoded CA Cert as PEM
- failed to append CA file
- failed to decode PEM certificate data
- URI is invalid
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/af5fa20bdeb7dd88.
Report an issue: GitHub.