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

  1. Inspect the file: openssl x509 -in <caFile> -noout -subject must succeed and print a subject.
  2. If it is DER, convert: openssl x509 -inform der -in ca.der -out ca.pem, then point the config at ca.pem.
  3. Make sure the file contains the full '-----BEGIN CERTIFICATE-----' ... '-----END CERTIFICATE-----' block(s), including the CA's cert not just its key.
  4. 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

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

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/10ad86a9f81f661c. Report an issue: GitHub.