goharbor/harbor · error

invalid CA certificate: no valid certificates found in PEM d

Error message

invalid CA certificate: no valid certificates found in PEM data

What it means

ValidateCACertificate in Harbor's common HTTP transport rejects a non-empty CA certificate string from which no valid x509 certificate could be parsed. normalizePEM first trims whitespace and normalizes line endings, then pem.Decode must find at least one block of type CERTIFICATE that x509.ParseCertificate accepts; zero parseable certificates triggers this error. Empty input is allowed (returns nil).

Source

Thrown at src/common/http/transport.go:125

	return cert
}

// ValidateCACertificate validates whether the provided CA certificate string
// contains at least one valid PEM-encoded x509 certificate.
func ValidateCACertificate(caCert string) error {
	caCert = normalizePEM(caCert)
	if caCert == "" {
		return nil
	}

	// Attempt to parse one or more certificates from the provided PEM
	certs, err := parseCertificatesFromPEM(caCert)
	if err != nil {
		return fmt.Errorf("invalid CA certificate: %w", err)
	}

	if len(certs) == 0 {
		return errors.New("invalid CA certificate: no valid certificates found in PEM data")
	}

	return nil
}

// parseCertificatesFromPEM decodes all PEM blocks and parses certificates.
func parseCertificatesFromPEM(pemData string) ([]*x509.Certificate, error) {
	var certs []*x509.Certificate
	rest := []byte(pemData)

	for {
		var block *pem.Block
		block, rest = pem.Decode(rest)
		if block == nil {
			break
		}
		if block.Type != "CERTIFICATE" {
			continue

View on GitHub (pinned to 7b2fd08cc5)

Solutions

  1. Verify the PEM locally: openssl x509 -in ca.crt -noout -text — if that fails, the PEM is bad
  2. Convert DER to PEM: openssl x509 -inform der -in ca.der -out ca.pem
  3. Strip the TRUSTED marker: openssl x509 -in trusted.crt -out plain.crt
  4. Ensure the block reads -----BEGIN CERTIFICATE----- / -----END CERTIFICATE----- with intact base64, and paste only certificate(s), never a private key

Example fix

# before: DER file pasted as 'CA certificate'
openssl x509 -inform der -in ca.der -out ca.pem   # convert DER -> PEM
openssl x509 -in ca.pem -noout -text              # must print cert details
# after: paste ca.pem's BEGIN/END CERTIFICATE block into the CA field
Defensive patterns

Strategy: validation

Validate before calling

// verify a CA PEM the same way Harbor does, before configuring it
func hasValidCertPEM(pemStr string) bool {
    rest := []byte(strings.TrimSpace(pemStr))
    found := false
    for {
        var block *pem.Block
        block, rest = pem.Decode(rest)
        if block == nil {
            return found
        }
        if block.Type == "CERTIFICATE" {
            if _, err := x509.ParseCertificate(block.Bytes); err != nil {
                return false
            }
            found = true
        }
    }
}

Prevention

When it happens

Trigger: Supplying a CA certificate that is DER (binary), a private key or CSR only, an OpenSSL 'TRUSTED CERTIFICATE' block, a PEM with mangled BEGIN/END headers, base64 garbage, or only non-CERTIFICATE PEM blocks (e.g. just the key pair's PRIVATE KEY block).

Common situations: Copy-pasting the wrong half of a keypair into a registry or replication endpoint CA field, Windows line-ending or truncated pastes, certificates exported as DER from Windows certmgr.

Understand the failure class

Related errors


AI-assisted analysis of goharbor/harbor@7b2fd08cc5 (2026-08-16). Data as JSON: /api/errors/9ec8476f3da16f23. Report an issue: GitHub.