{"record":{"id":"10ad86a9f81f661c","repo":"fatedier/frp","slug":"failed-to-parse-ca-certificate-from-file-q-no-va","errorCode":null,"errorMessage":"failed to parse CA certificate from file %q: no valid PEM certificates found","messagePattern":"failed to parse CA certificate from file %q: no valid PEM certificates found","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/transport/tls.go","lineNumber":90,"sourceCode":"\n\ttlsCert, err := tls.X509KeyPair(certPEM, keyPEM)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &tlsCert, nil\n}\n\n// Only support one ca file to add\nfunc newCertPool(caPath string) (*x509.CertPool, error) {\n\tpool := x509.NewCertPool()\n\n\tcaCrt, err := os.ReadFile(caPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !pool.AppendCertsFromPEM(caCrt) {\n\t\treturn nil, fmt.Errorf(\"failed to parse CA certificate from file %q: no valid PEM certificates found\", caPath)\n\t}\n\n\treturn pool, nil\n}\n\nfunc NewServerTLSConfig(certPath, keyPath, caPath string) (*tls.Config, error) {\n\tbase := &tls.Config{}\n\n\tif certPath == \"\" || keyPath == \"\" {\n\t\t// server will generate tls conf by itself\n\t\tcert, err := newRandomTLSKeyPair()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tbase.Certificates = []tls.Certificate{*cert}\n\t} else {\n\t\tcert, err := newCustomTLSKeyPair(certPath, keyPath)\n\t\tif err != nil {","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/transport/tls.go#L72-L108","documentation":"newCertPool reads the CA file with os.ReadFile and requires x509.CertPool.AppendCertsFromPEM to accept at least one certificate. If the file exists and reads fine but contains zero parsable PEM CERTIFICATE blocks, this error is returned. Used for both client-side (tls.trustedCaFile) and server-side mTLS CA pools.","triggerScenarios":"Pointing trustedCaFile at a private key file, a CSR, a DER/binary-format certificate, a YAML/TOML file that merely embeds the cert (extra non-PEM text is tolerated, but no PEM cert means failure), or an empty file.","commonSituations":"Confusing server.crt with ca.crt; certs issued in DER format from some CAs; copy-paste that lost the BEGIN/END CERTIFICATE lines; Docker secrets mounted as the wrong file; trailing corruption from editors.","solutions":["Inspect the file: openssl x509 -in <caFile> -noout -subject must succeed and print a subject.","If it is DER, convert: openssl x509 -inform der -in ca.der -out ca.pem, then point the config at ca.pem.","Make sure the file contains the full '-----BEGIN CERTIFICATE-----' ... '-----END CERTIFICATE-----' block(s), including the CA's cert not just its key.","Give the CA file (not the key, not the leaf cert) — e.g. the root/intermediate that signed the peer."],"exampleFix":"# before (DER cert)\ntls.trustedCaFile = \"/etc/frp/ca.crt\"   # binary DER\n\n# after\nopenssl x509 -inform der -in /etc/frp/ca.crt -out /etc/frp/ca.pem\ntls.trustedCaFile = \"/etc/frp/ca.pem\"","handlingStrategy":"validation","validationCode":"// fail fast at startup instead of at first TLS handshake\nfunc validateCAPEM(path string) error {\n    data, err := os.ReadFile(path)\n    if err != nil {\n        return err\n    }\n    if !x509.NewCertPool().AppendCertsFromPEM(data) {\n        return fmt.Errorf(\"%s contains no valid PEM certificates\", path)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run `openssl x509 -in <file> -noout` on every cert before referencing it in config.","Standardize on PEM; convert DER once at provisioning time.","Never point trustedCaFile at a key, CSR, or leaf-only bundle — use the CA that signed the peer."],"tags":["tls","certificates","pem","configuration"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}