quarkusio/quarkus · error · IOException

Invalid PEM file: No PEM content found.

Error message

Invalid PEM file: No PEM content found.

What it means

loadCertificateFromPEM parses a PEM file using BouncyCastle's PemReader. If the file contains no recognizable PEM block (a line like '-----BEGIN ...-----'), readPemObject() returns null and the method throws this IOException. It signals the file is not a valid PEM-encoded certificate.

Source

Thrown at extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/letsencrypt/LetsEncryptHelpers.java:69

        AUDIT.debug("Writing certificate chain to file: " + certificateChainFile.getAbsolutePath());

        if (chain.length == 1) {
            CertificateUtils.writeCertificateToPEM(chain[0], certificateChainFile);
            return;
        }

        // For some reason the method from CertificateUtils distinguishes the first certificate and the rest of the chain
        X509Certificate[] restOfTheChain = new X509Certificate[chain.length - 1];
        System.arraycopy(chain, 1, restOfTheChain, 0, chain.length - 1);
        CertificateUtils.writeCertificateToPEM(chain[0], certificateChainFile, restOfTheChain);
    }

    public static X509Certificate loadCertificateFromPEM(String pemFilePath) throws IOException, CertificateException {
        try (PemReader pemReader = new PemReader(new FileReader(pemFilePath))) {
            PemObject pemObject = pemReader.readPemObject();
            if (pemObject == null) {
                throw new IOException("Invalid PEM file: No PEM content found.");
            }
            byte[] content = pemObject.getContent();
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            return (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(content));
        }
    }

    public static String createAccount(AcmeClient acmeClient,
            String letsEncryptPath,
            boolean staging,
            String contactEmail,
            String acmeServerUrl,
            String acmeStagingServerUrl) {
        LOGGER.infof("\uD83D\uDD35 Creating %s ACME account", (staging ? "staging" : "production"));

        // Use defaults if not specified
        String serverUrl = acmeServerUrl != null ? acmeServerUrl
                : DEFAULT_ACME_URL;

View on GitHub (pinned to e1c734241f)

Solutions

  1. Open the file and confirm it contains '-----BEGIN CERTIFICATE-----' and '-----END CERTIFICATE-----' lines
  2. Convert a DER certificate to PEM: openssl x509 -inform der -in cert.der -out cert.pem
  3. Re-export/re-download the certificate in PEM (base64) format from your CA
  4. Verify the file path points to the actual certificate, not a keystore or private key file
  5. Check the file is not empty or truncated (file size, cat the file)

Example fix

// before (file is DER)
X509Certificate cert = LetsEncryptHelpers.loadCertificateFromPEM("cert.der");
// after: convert first
// openssl x509 -inform der -in cert.der -out cert.pem
X509Certificate cert = LetsEncryptHelpers.loadCertificateFromPEM("cert.pem");
Defensive patterns

Strategy: validation

Validate before calling

boolean isPem(String path) throws IOException {
    String content = Files.readString(Paths.get(path));
    return content.contains("-----BEGIN CERTIFICATE-----") && content.contains("-----END CERTIFICATE-----");
}
if (!isPem(pemFilePath)) throw new IllegalArgumentException("Not a PEM certificate: " + pemFilePath);
X509Certificate cert = LetsEncryptHelpers.loadCertificateFromPEM(pemFilePath);

Type guard

boolean looksLikePem(byte[] bytes) {
    String head = new String(bytes, 0, Math.min(bytes.length, 64), StandardCharsets.US_ASCII).trim();
    return head.startsWith("-----BEGIN ");
}

Try / catch

try {
    X509Certificate cert = LetsEncryptHelpers.loadCertificateFromPEM(path);
} catch (IOException | CertificateException e) {
    // e.getMessage() == "Invalid PEM file: No PEM content found."
    throw new IllegalArgumentException("File is not PEM-encoded: " + path + ". Convert with: openssl x509 -inform der -in file -out file.pem", e);
}

Prevention

When it happens

Trigger: Calling LetsEncryptHelpers.loadCertificateFromPEM with a path to a file whose content has no '-----BEGIN...'/'-----END...' delimiters — e.g. a DER-encoded cert, a private key in another format, an empty file, or an HTML error page saved as .pem.

Common situations: Downloading a certificate and accidentally saving the HTML error response; exporting a certificate as DER (.der/.crt binary) but naming it .pem; a truncated or corrupted download; pointing the tool at a PKCS#12/JKS keystore instead of a PEM file.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/753ae4bdc21a26aa. Report an issue: GitHub.