{"record":{"id":"9d073610b89ef33f","repo":"quarkusio/quarkus","slug":"the-private-key-cannot-be-null","errorCode":null,"errorMessage":"The private key cannot be null","messagePattern":"The private key cannot be null","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/letsencrypt/LetsEncryptHelpers.java","lineNumber":43,"sourceCode":"import org.wildfly.security.x500.cert.acme.AcmeAccount;\nimport org.wildfly.security.x500.cert.acme.AcmeException;\n\nimport io.smallrye.certs.CertificateUtils;\nimport io.vertx.core.json.JsonObject;\n\npublic class LetsEncryptHelpers {\n\n    public static final String DEFAULT_ACME_URL = \"https://acme-v02.api.letsencrypt.org/directory\";\n    public static final String DEFAULT_ACME_STAGING_URL = \"https://acme-staging-v02.api.letsencrypt.org/directory\";\n    public static final String TLS_AUDIT_LOG = \"io.quarkus.tls.audit\";\n\n    static Logger LOGGER = Logger.getLogger(LetsEncryptHelpers.class);\n    public static Logger AUDIT = Logger.getLogger(LetsEncryptHelpers.TLS_AUDIT_LOG);\n\n    public static void writePrivateKeyAndCertificateChainsAsPem(PrivateKey pk, X509Certificate[] chain, File privateKeyFile,\n            File certificateChainFile) throws Exception {\n        if (pk == null) {\n            throw new IllegalArgumentException(\"The private key cannot be null\");\n        }\n        if (chain == null || chain.length == 0) {\n            throw new IllegalArgumentException(\"The certificate chain cannot be null or empty\");\n        }\n\n        AUDIT.debug(\"Writing private key to file: \" + privateKeyFile.getAbsolutePath());\n        CertificateUtils.writePrivateKeyToPem(pk, null, privateKeyFile);\n\n        AUDIT.debug(\"Writing certificate chain to file: \" + certificateChainFile.getAbsolutePath());\n\n        if (chain.length == 1) {\n            CertificateUtils.writeCertificateToPEM(chain[0], certificateChainFile);\n            return;\n        }\n\n        // For some reason the method from CertificateUtils distinguishes the first certificate and the rest of the chain\n        X509Certificate[] restOfTheChain = new X509Certificate[chain.length - 1];\n        System.arraycopy(chain, 1, restOfTheChain, 0, chain.length - 1);","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/letsencrypt/LetsEncryptHelpers.java#L25-L61","documentation":"LetsEncryptHelpers.writePrivateKeyAndCertificateChainsAsPem persists the ACME-issued private key and certificate chain as PEM files. As a fail-fast precondition it throws IllegalArgumentException when the PrivateKey argument is null. This almost always means the upstream ACME result (X509CertificateChainAndSigningKey.getSigningKey()) produced no signing key, i.e. certificate issuance did not actually complete.","triggerScenarios":"Calling writePrivateKeyAndCertificateChainsAsPem(null, chain, keyFile, certFile) directly, or via issueCertificate when obtainCertificateChain returns an X509CertificateChainAndSigningKey whose getSigningKey() is null (failed/partial ACME order, empty renewal result).","commonSituations":"Tooling/tests invoking the helper with an unset key; a custom or broken ACME server returning a chain without a corresponding signing key; calling issueCertificate on an order that silently failed; deserializing a stored account/key pair that is missing the private-key field.","solutions":["Null-check the key before calling: if certChainAndPrivateKey.getSigningKey() == null, abort — the certificate issuance failed and must be retried.","Re-run the certificate issuance (lets-encrypt issue/renew) so a complete chain+key is obtained from the ACME server.","Verify the ACME server actually issued a key; with custom servers, test against the Let's Encrypt staging URL first.","Check the account.json in the lets-encrypt directory contains a private-key entry if account-key reuse is involved."],"exampleFix":"// before\nLetsEncryptHelpers.writePrivateKeyAndCertificateChainsAsPem(\n    certChainAndPrivateKey.getSigningKey(),\n    certChainAndPrivateKey.getCertificateChain(), keyPem, chainPem);\n\n// after\nPrivateKey key = certChainAndPrivateKey.getSigningKey();\nif (key == null || certChainAndPrivateKey.getCertificateChain() == null\n        || certChainAndPrivateKey.getCertificateChain().length == 0) {\n    throw new IllegalStateException(\"ACME order did not return a key/certificate; retry issuance\");\n}\nLetsEncryptHelpers.writePrivateKeyAndCertificateChainsAsPem(key,\n    certChainAndPrivateKey.getCertificateChain(), keyPem, chainPem);","handlingStrategy":"validation","validationCode":"PrivateKey key = certChainAndPrivateKey.getSigningKey();\nif (key == null) {\n    throw new IllegalStateException(\"ACME returned no signing key; issuance failed\");\n}\nLetsEncryptHelpers.writePrivateKeyAndCertificateChainsAsPem(key,\n    certChainAndPrivateKey.getCertificateChain(), keyPem, chainPem);","typeGuard":"static boolean hasUsableSigningKey(X509CertificateChainAndSigningKey r) {\n    return r != null && r.getSigningKey() != null\n        && r.getCertificateChain() != null && r.getCertificateChain().length > 0;\n}","tryCatchPattern":"try {\n    LetsEncryptHelpers.writePrivateKeyAndCertificateChainsAsPem(pk, chain, keyPem, chainPem);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"private key cannot be null\")) {\n        // issuance incomplete: log and trigger a fresh obtainCertificateChain\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always guard X509CertificateChainAndSigningKey results from obtainCertificateChain before writing files.","Re-run issuance rather than persisting partial results.","Verify account.json contains the private-key field when reusing accounts.","Test custom ACME integrations against Let's Encrypt staging first."],"tags":["acme","lets-encrypt","pem","null-argument","precondition"],"backgroundTag":"null-argument-precondition","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}