{"record":{"id":"2c27d5d46efb0bca","repo":"larksuite/cli","slug":"invalid-s-q-no-certificates-parsed-from-pem","errorCode":null,"errorMessage":"invalid %s %q: no certificates parsed from PEM","messagePattern":"invalid (.+?) %q: no certificates parsed from PEM","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/transport/tls_ca.go","lineNumber":54,"sourceCode":"\t}\n\tpemBytes, err := vfs.ReadFile(safeCAPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read %s %q: %w\", envvars.CliCAPath, caPath, err)\n\t}\n\n\t// Augment the system trust store. Do NOT silently discard a SystemCertPool\n\t// error: falling back to an empty pool would make this transport trust ONLY\n\t// the extra CA (dropping all system roots), which narrows trust unexpectedly\n\t// and could break TLS to legitimate endpoints. Fail closed instead.\n\tpool, err := x509.SystemCertPool()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to load system cert pool for %s: %w\", envvars.CliCAPath, err)\n\t}\n\tif pool == nil {\n\t\tpool = x509.NewCertPool()\n\t}\n\tif ok := pool.AppendCertsFromPEM(pemBytes); !ok {\n\t\treturn fmt.Errorf(\"invalid %s %q: no certificates parsed from PEM\", envvars.CliCAPath, caPath)\n\t}\n\n\tif t.TLSClientConfig == nil {\n\t\tt.TLSClientConfig = &tls.Config{}\n\t} else {\n\t\t// Clone to avoid mutating shared config from the base transport.\n\t\tt.TLSClientConfig = t.TLSClientConfig.Clone()\n\t}\n\tif t.TLSClientConfig.MinVersion == 0 || t.TLSClientConfig.MinVersion < tls.VersionTLS12 {\n\t\tt.TLSClientConfig.MinVersion = tls.VersionTLS12\n\t}\n\tt.TLSClientConfig.RootCAs = pool\n\treturn nil\n}\n","sourceCodeStart":36,"sourceCodeEnd":69,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/transport/tls_ca.go#L36-L69","documentation":"The extra root CA file was read but AppendCertsFromPEM parsed zero certificates from its contents. This means the file is not valid PEM (wrong format, DER-encoded binary, empty, or garbage), so the CLI refuses to add it to the trust pool.","triggerScenarios":"CLI_CA_PATH points to a file that exists and is readable, but its bytes contain no '-----BEGIN CERTIFICATE-----' PEM blocks; raised in applyExtraRootCA after pool.AppendCertsFromPEM returns false.","commonSituations":"Downloading a DER (.cer/.crt binary) cert instead of PEM; saving an HTML error page as ca.pem; concatenating only private keys or an expired/empty bundle.","solutions":["Convert DER to PEM: openssl x509 -inform der -in cert.cer -out ca.pem.","Verify the file contains PEM blocks: openssl x509 -in ca.pem -noout -subject.","Re-export the certificate in PEM format from the issuing source and re-point CLI_CA_PATH at it."],"exampleFix":"// before\nopenssl s_client -showcerts ... > ca.pem   # may capture non-PEM noise\n// after\nopenssl x509 -inform der -in cert.cer -out /etc/lark-cli/ca.pem","handlingStrategy":"validation","validationCode":"pemBytes, err := os.ReadFile(caPath)\nif err != nil { return err }\nif !x509.NewCertPool().AppendCertsFromPEM(pemBytes) {\n\treturn fmt.Errorf(\"%s contains no PEM certificates\", caPath)\n}","typeGuard":"func isPEMCertificate(path string) bool {\n\tb, err := os.ReadFile(path)\n\tif err != nil { return false }\n\tblock, _ := pem.Decode(b)\n\treturn block != nil && block.Type == \"CERTIFICATE\"\n}","tryCatchPattern":null,"preventionTips":["Convert DER certs to PEM with 'openssl x509 -inform der' before use.","Validate with 'openssl x509 -in ca.pem -noout -subject' during provisioning.","Store only certificate bundles, never pages or keys, at CLI_CA_PATH."],"tags":["tls","certificate","parsing"],"backgroundTag":"invalid-pem-certificate","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}