quarkusio/quarkus · error · RuntimeException

Failure to copy certificate pem: + ex.getMessage()

Error message

Failure to copy certificate pem: + ex.getMessage()

What it means

After the certificate chain is obtained, issueCertificate writes the private key and certificate chain as PEM files via writePrivateKeyAndCertificateChainsAsPem. Any exception during this conversion/writing step is wrapped in this RuntimeException (message includes the underlying ex.getMessage(), despite the misplaced quote in the literal). It indicates a local I/O or encoding failure, not an ACME problem.

Source

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

            certChainAndPrivateKey = acmeClient.obtainCertificateChain(acmeAccount, staging, domain);
            AUDIT.info("Certificate chain obtained successfully - domain: " + domain + ", chain-length: "
                    + certChainAndPrivateKey.getCertificateChain().length);
        } catch (AcmeException t) {
            AUDIT.error("Failed to obtain certificate - domain: " + domain + ", staging: " + staging, t);
            throw new RuntimeException("Failed to obtain certificate: " + t.getMessage(), t);
        }
        LOGGER.info("\uD83D\uDD35 Certificate and private key issued, converting them to PEM files");

        AUDIT.info("Writing certificate to: " + certChainPemLoc.getAbsolutePath());
        AUDIT.info("Writing private key to: " + privateKeyPemLoc.getAbsolutePath());

        try {
            LetsEncryptHelpers.writePrivateKeyAndCertificateChainsAsPem(certChainAndPrivateKey.getSigningKey(),
                    certChainAndPrivateKey.getCertificateChain(), privateKeyPemLoc, certChainPemLoc);
        } catch (Exception ex) {
            AUDIT.error("Failed to write certificate files - cert: " + certChainPemLoc + ", key: "
                    + privateKeyPemLoc, ex);
            throw new RuntimeException("Failure to copy certificate pem: " + ex.getMessage(), ex);
        }
    }

    private static AcmeAccount getAccount(File letsEncryptPath, String acmeServerUrl, String acmeStagingServerUrl) {
        LOGGER.debugf("Getting account from %s", letsEncryptPath);

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

        JsonObject json = readAccountJson(letsEncryptPath);
        AcmeAccount.Builder builder = AcmeAccount.builder().setTermsOfServiceAgreed(true)
                .setServerUrl(serverUrl)
                .setStagingServerUrl(stagingServerUrl);

        String keyAlgorithm = json.getString("key-algorithm");

View on GitHub (pinned to e1c734241f)

Solutions

  1. Ensure the parent directories of certChainPemLoc and privateKeyPemLoc exist and are writable
  2. Check file permissions on the existing pem files (they may be read-only or owned by another user)
  3. Free disk space if the filesystem is full
  4. Inspect the wrapped cause in the stack trace for the exact write/encode failure

Example fix

// before
File cert = new File("/missing/dir/cert.pem");
LetsEncryptHelpers.issueCertificate(..., cert, key, ...); // RuntimeException: Failure to copy certificate pem
// after
new File("/missing/dir").mkdirs();
LetsEncryptHelpers.issueCertificate(..., cert, key, ...);
Defensive patterns

Strategy: validation

Validate before calling

File cert = certChainPemLoc, key = privateKeyPemLoc;
for (File f : new File[]{cert, key}) {
    File parent = f.getAbsoluteFile().getParentFile();
    if (parent == null || (!parent.exists() && !parent.mkdirs()) || !parent.canWrite()) {
        throw new IllegalStateException("Cannot write to directory: " + parent);
    }
    if (f.exists() && !f.canWrite()) throw new IllegalStateException("File not writable: " + f);
}

Try / catch

try {
    LetsEncryptHelpers.issueCertificate(acmeClient, letsEncryptPath, staging, domain, keyLoc, certLoc);
} catch (RuntimeException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Failure to copy certificate pem:")) {
        throw new IllegalStateException("Could not write cert/key to " + certLoc + "/" + keyLoc + ": " + e.getMessage(), e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling issueCertificate/renewCertificate when the target certChainPemLoc or privateKeyPemLoc files/directories cannot be written — bad path, missing parent directory, permissions — or the key/certificate cannot be PEM-encoded.

Common situations: Certificate output directory does not exist; the process lacks write permission to the target path; disk full; the existing target file is locked or read-only.

Understand the failure class

Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.

Related errors


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