fatedier/frp · error
failed to parse CA certificate from file %q: no valid PEM ce
Error message
failed to parse CA certificate from file %q: no valid PEM certificates found
What it means
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.
Source
Thrown at pkg/transport/tls.go:90
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
if err != nil {
return nil, err
}
return &tlsCert, nil
}
// Only support one ca file to add
func newCertPool(caPath string) (*x509.CertPool, error) {
pool := x509.NewCertPool()
caCrt, err := os.ReadFile(caPath)
if err != nil {
return nil, err
}
if !pool.AppendCertsFromPEM(caCrt) {
return nil, fmt.Errorf("failed to parse CA certificate from file %q: no valid PEM certificates found", caPath)
}
return pool, nil
}
func NewServerTLSConfig(certPath, keyPath, caPath string) (*tls.Config, error) {
base := &tls.Config{}
if certPath == "" || keyPath == "" {
// server will generate tls conf by itself
cert, err := newRandomTLSKeyPair()
if err != nil {
return nil, err
}
base.Certificates = []tls.Certificate{*cert}
} else {
cert, err := newCustomTLSKeyPair(certPath, keyPath)
if err != nil {View on GitHub (pinned to 6c8a8d0a97)
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.
Example fix
# before (DER cert) tls.trustedCaFile = "/etc/frp/ca.crt" # binary DER # after openssl x509 -inform der -in /etc/frp/ca.crt -out /etc/frp/ca.pem tls.trustedCaFile = "/etc/frp/ca.pem"
Defensive patterns
Strategy: validation
Validate before calling
// fail fast at startup instead of at first TLS handshake
func validateCAPEM(path string) error {
data, err := os.ReadFile(path)
if err != nil {
return err
}
if !x509.NewCertPool().AppendCertsFromPEM(data) {
return fmt.Errorf("%s contains no valid PEM certificates", path)
}
return nil
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- gen TLS config error: %v
- non-TLS connection received on a TlsOnly server
- ErrHealthCheckType
- unit not support
- type is required
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/10ad86a9f81f661c.
Report an issue: GitHub.