puppetlabs/puppet · error · OpenSSL::X509::CertificateError
Failed to parse CA certificates as PEM
Error message
Failed to parse CA certificates as PEM
What it means
Raised by CertProvider#load_cacerts_from_pem as OpenSSL::X509::CertificateError when the CA file exists but its text does not match CERT_DELIMITERS, i.e. it contains no '-----BEGIN CERTIFICATE-----' block. The same error class also surfaces from OpenSSL::X509::Certificate.new inside the scan block when an individual block is corrupt or truncated. Either way the trust store cannot be built and TLS setup aborts.
Source
Thrown at lib/puppet/x509/cert_provider.rb:77
if !pem && required
raise Puppet::Error, _("The CA certificates are missing from '%{path}'") % { path: @capath }
end
pem ? load_cacerts_from_pem(pem) : nil
rescue SystemCallError => e
raise Puppet::Error.new(_("Failed to load CA certificates from '%{capath}'") % { capath: @capath }, e)
end
# Load PEM encoded CA certificates.
#
# @param pem [String] PEM encoded certificate(s)
# @return [Array<OpenSSL::X509::Certificate>] Array of CA certs
# @raise [OpenSSL::X509::CertificateError] The `pem` text does not contain a valid cert
#
# @api private
def load_cacerts_from_pem(pem)
# TRANSLATORS 'PEM' is an acronym and shouldn't be translated
raise OpenSSL::X509::CertificateError, _("Failed to parse CA certificates as PEM") if pem !~ CERT_DELIMITERS
pem.scan(CERT_DELIMITERS).map do |text|
OpenSSL::X509::Certificate.new(text)
end
end
# Save `crls` to the configured `crlpath`.
#
# @param crls [Array<OpenSSL::X509::CRL>] Array of CRLs to save
# @raise [Puppet::Error] if the CRLs cannot be saved
#
# @api private
def save_crls(crls)
save_pem(crls.map(&:to_pem).join, @crlpath, **permissions_for_setting(:hostcrl))
rescue SystemCallError => e
raise Puppet::Error.new(_("Failed to save CRLs to '%{crlpath}'") % { crlpath: @crlpath }, e)
end
View on GitHub (pinned to e227c27540)
Solutions
- Inspect the file: `head ca.pem` must show BEGIN/END CERTIFICATE lines and `openssl x509 -in ca.pem -noout` must parse
- If it is DER, convert: `openssl x509 -inform der -in ca.der -outform pem -out ca.pem`
- Re-download the CA cert from the Puppet server CA and atomically overwrite the file
- Verify every BEGIN/END pair when multiple CAs are concatenated, then rerun `puppet agent -t`
Example fix
# before: file holds DER bytes $ openssl x509 -in ca.pem -noout unable to load certificate # after $ openssl x509 -inform der -in ca.der -outform pem -out ca.pem $ openssl x509 -in ca.pem -noout subject=CN=Puppet CA: puppet.example.com
Defensive patterns
Strategy: validation
Validate before calling
def pem_cert?(path)
File.read(path).match?(/-----BEGIN CERTIFICATE-----/)
end
raise "#{Puppet[:localcacert]} is not PEM" unless pem_cert?(Puppet[:localcacert]) Try / catch
begin
certs = Puppet::X509::CertProvider.new.load_cacerts(required: true)
rescue OpenSSL::X509::CertificateError => e
abort "CA bundle at #{Puppet[:localcacert]} is not valid PEM: #{e.message}"
end Prevention
- Verify fetched certs with `openssl x509 -noout` before atomically moving them into ssldir
- Never hand-edit ca.pem; replace it wholesale from the CA
- CI-check ssl artifacts in images for well-formed PEM blocks
When it happens
Trigger: load_cacerts(required: true) reads a localcacert whose PEM has no certificate delimiters, or a block OpenSSL cannot parse: binary DER data saved with a .pem name, an HTML/proxy error page captured instead of the cert, a truncated download, or cert and key files swapped.
Common situations: curl/wget fetch of the CA that hit a proxy login page; DER-formatted certs from a vendor; files mangled by Windows copies or templating that strips newlines; hand-editing ca.pem and losing the END line.
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
- Failed to parse CRLs as PEM
- PathPatterns cannot be created with a zero byte.
- The CA certificates are missing from '%{path}'
- The CRL is missing from '%{path}'
- The private key is missing from '%{path}'
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/324ed2e01eeffed5.
Report an issue: GitHub.