{"record":{"id":"22a73cdeb37130a4","repo":"temporalio/temporal","slug":"unable-to-read-client-ca-file","errorCode":null,"errorMessage":"unable to read client ca file","messagePattern":"unable to read client ca file","errorType":"validation","errorClass":"ErrTLSConfig","httpStatus":null,"severity":"error","filePath":"common/auth/tls_config_helper.go","lineNumber":146,"sourceCode":"\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 != \"\" {\n\t\tcaBytes, err = os.ReadFile(temporalTls.CaFile)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s (%w)\", ErrTLSConfig, \"unable to read client ca file\", err)\n\t\t}\n\t} else if temporalTls.CaData != \"\" {\n\t\tcaBytes, err = base64.StdEncoding.DecodeString(temporalTls.CaData)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s (%w)\", ErrTLSConfig, \"unable to decode client ca data\", err)\n\t\t}\n\t}\n\tif len(caBytes) > 0 {\n\t\tcaCertPool := x509.NewCertPool()\n\t\tcaCerts, err := parseCertsFromPEM(caBytes)\n\t\tif len(caCerts) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s (%w)\", ErrTLSConfig, \"unable to parse certs as PEM\", err)\n\t\t}\n\t\tfor _, cert := range caCerts {\n\t\t\tcaCertPool.AddCert(cert)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s (%w)\", ErrTLSConfig, \"unable to load decoded CA Cert as PEM\", err)","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/auth/tls_config_helper.go#L128-L164","documentation":"parseCAs returns this error when CaFile is set but os.ReadFile fails to read the CA PEM file. The original os error is chained in, so the message includes the underlying reason (missing file, permissions, etc.) and everything is wrapped with ErrTLSConfig.","triggerScenarios":"NewTLSConfig -> parseCAs with CaFile pointing to a path that does not exist, is a directory, or is unreadable by the process (permission denied).","commonSituations":"Typo in the CA path; config mounted into the container at a different path than referenced; file permissions after mounting a Kubernetes secret (non-root user cannot read); relative path resolved against a different working directory than expected in the service.","solutions":["Verify the path in CaFile exists and is readable by the service user (ls -l; fix permissions/ownership).","Correct the path in config — use an absolute path and confirm the mount location inside the container.","Check the chained os error in the message: ENOENT means wrong path, EACCES means permissions.","If the CA is intended inline, remove CaFile and set base64-encoded CaData instead."],"exampleFix":"// before\ntls:\n  caFile: \"/etc/temporal/certs/ca.cr t\"\n// after\ntls:\n  caFile: \"/etc/temporal/certs/ca.pem\"","handlingStrategy":"validation","validationCode":"func checkCaFileReadable(path string) error {\n\tinfo, err := os.Stat(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"caFile not accessible: %w\", err)\n\t}\n\tif info.IsDir() {\n\t\treturn fmt.Errorf(\"caFile is a directory: %s\", path)\n\t}\n\tf, err := os.Open(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"caFile not readable: %w\", err)\n\t}\n\treturn f.Close()\n}","typeGuard":null,"tryCatchPattern":"tlsCfg, err := auth.NewTLSConfig(cfg)\nif err != nil {\n\tif errors.Is(err, auth.ErrTLSConfig) {\n\t\tlogger.Error(\"TLS configuration invalid\", tag.Key, err) // inspect chained os error\n\t\treturn err\n\t}\n\treturn err\n}","preventionTips":["Use absolute CA paths and verify they exist inside the actual container image/mounts","Check file ownership/permissions for the service's non-root user","Add a readiness check that stats configured cert/CA paths before serving","Prefer mounting the CA as a Kubernetes secret volume at a documented, fixed path"],"tags":["tls","filesystem","config"],"backgroundTag":"file-not-found","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}