quarkusio/quarkus · error · RuntimeException

Failure to save the account

Error message

Failure to save the account

What it means

saveAccount persists the ACME account JSON to <letsEncryptPath>/account.json via Files.copy with REPLACE_EXISTING. Any IOException while writing (bad directory, permissions, disk full) is wrapped in this RuntimeException. Without the account file, later certificate operations cannot authenticate.

Solutions

  1. Ensure the letsEncrypt directory exists before creating the account: mkdir -p <letsencrypt-dir>
  2. Check write permissions on the directory (and run as a user with access)
  3. Verify the filesystem is not read-only or full (df -h, mount options)
  4. If the error persists, inspect the wrapped cause ('Caused by' IOException) for the exact filesystem failure

Example fix

// before
LetsEncryptHelpers.createAccount(acmeClient, new File("/nonexistent/letsencrypt"), termsAgreed, ...); // RuntimeException
// after
File dir = new File("/etc/quarkus/letsencrypt");
dir.mkdirs();
LetsEncryptHelpers.createAccount(acmeClient, dir, termsAgreed, ...);
Defensive patterns

Strategy: validation

Validate before calling

File dir = letsEncryptPath;
if (!dir.exists() || !dir.isDirectory()) {
    dir.mkdirs();
}
if (!dir.canWrite()) {
    throw new IllegalStateException("Directory not writable: " + dir);
}
LetsEncryptHelpers.createAccount(acmeClient, dir, termsAgreed, email);

Try / catch

try {
    LetsEncryptHelpers.createAccount(acmeClient, letsEncryptDir, termsAgreed, email);
} catch (RuntimeException e) {
    if (e.getCause() instanceof IOException) {
        throw new IllegalStateException("Cannot write ACME account to " + letsEncryptDir + ": " + e.getCause().getMessage(), e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling createAccount (which calls saveAccount) when letsEncryptPath does not exist or is not writable — e.g. directory missing, read-only filesystem, or insufficient file permissions.

Common situations: Running the CLI as a user without write access to the config directory; letsencrypt directory not created beforehand; running in a container with a read-only filesystem; disk full on the host.

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/322a51ec94c80c92. Report an issue: GitHub.

Appendix: source

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

        if (acmeAccount.getKeyAlgorithmName() != null) {
            json.put("key-algorithm", acmeAccount.getKeyAlgorithmName());
        }
        json.put("key-size", acmeAccount.getKeySize());
        return json;
    }

    private static void saveAccount(String letsEncryptPath, JsonObject accountJson) {
        LOGGER.debugf("Saving account to %s", letsEncryptPath);

        // If more than one account must be supported, we can save accounts to unique files in .lets-encrypt/accounts
        // and require an account alias/id during operations requiring an account
        java.nio.file.Path accountPath = Paths.get(letsEncryptPath + "/account.json");
        try {
            AUDIT.debug("Writing ACME account to file: " + accountPath.toString());
            Files.copy(new ByteArrayInputStream(accountJson.encode().getBytes(StandardCharsets.US_ASCII)), accountPath,
                    StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException ex) {
            throw new RuntimeException("Failure to save the account", ex);
        }
    }

    public static void issueCertificate(
            AcmeClient acmeClient,
            File letsEncryptPath,
            boolean staging,
            String domain,
            File certChainPemLoc,
            File privateKeyPemLoc,
            String acmeServerUrl,
            String acmeStagingServerUrl) {
        AcmeAccount acmeAccount = getAccount(letsEncryptPath, acmeServerUrl, acmeStagingServerUrl);

        AUDIT.info("Requesting certificate - domain: " + domain + ", staging: " + staging + ", server: "
                + (acmeServerUrl != null ? acmeServerUrl : "default"));

        X509CertificateChainAndSigningKey certChainAndPrivateKey;

View on GitHub (pinned to e1c734241f)