apache/pulsar · critical · IllegalArgumentException

Failed to load private key from privateKey or privateKeyPath

Error message

Failed to load private key from privateKey or privateKeyPath field

What it means

After parsing authParams, setAuthParams resolves the private key from either the privateKey or privateKeyPath field. If both fields are absent/blank, or loadPrivateKey returns null (unreadable/unparseable key), AuthenticationAthenz throws IllegalArgumentException('Failed to load private key from privateKey or privateKeyPath field').

Source

Thrown at pulsar-client-auth-athenz/src/main/java/org/apache/pulsar/client/impl/auth/AuthenticationAthenz.java:239

        if (isNotBlank(authParams.get("x509CertChain"))) {
            // When using Copper Argos
            checkRequiredParams(authParams, "privateKey", "caCert", "providerDomain");
            // Absolute paths are required to generate a key refresher, so if these are relative paths, convert them
            this.x509CertChainPath = getAbsolutePathFromUrl(authParams.get("x509CertChain"));
            this.privateKeyPath = getAbsolutePathFromUrl(authParams.get("privateKey"));
            this.caCertPath = getAbsolutePathFromUrl(authParams.get("caCert"));
        } else {
            checkRequiredParams(authParams, "tenantDomain", "tenantService", "providerDomain");

            // privateKeyPath is deprecated, this is for compatibility
            if (isBlank(authParams.get("privateKey")) && isNotBlank(authParams.get("privateKeyPath"))) {
                this.privateKey = loadPrivateKey(authParams.get("privateKeyPath"));
            } else {
                this.privateKey = loadPrivateKey(authParams.get("privateKey"));
            }

            if (this.privateKey == null) {
                throw new IllegalArgumentException(
                        "Failed to load private key from privateKey or privateKeyPath field");
            }
        }

        if (isNotBlank(authParams.get("athenzConfPath"))) {
            System.setProperty("athenz.athenz_conf", authParams.get("athenzConfPath"));
        }
        if (isNotBlank(authParams.get("principalHeader"))) {
            System.setProperty("athenz.auth.principal.header", authParams.get("principalHeader"));
        }
        if (isNotBlank(authParams.get("roleHeader"))) {
            this.roleHeader = authParams.get("roleHeader");
            System.setProperty("athenz.auth.role.header", this.roleHeader);
        }
        if (isNotBlank(authParams.get("ztsUrl"))) {
            this.ztsUrl = authParams.get("ztsUrl");
        }
        if (isNotBlank(authParams.get("ztsProxyUrl"))) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Add a valid 'privateKey' entry (e.g. 'data:application/x-pem-file;base64,<key>') or a 'privateKeyPath' file URL ('file:///path/to/key.pem') to authParams
  2. Verify the referenced file/URL exists and contains a parseable PEM private key
  3. Check JSON key spelling — must be exactly 'privateKey' or 'privateKeyPath'

Example fix

// before
{"tenant":"t","service":"s"}
// after
{"tenant":"t","service":"s","privateKeyPath":"file:///etc/pulsar/athenz_priv_key.pem"}
Defensive patterns

Strategy: validation

Validate before calling

Map<String,String> params = new ObjectMapper().readValue(json, new TypeReference<Map<String,String>>(){});
String key = params.getOrDefault("privateKey", params.get("privateKeyPath"));
if (key == null || key.isBlank()) {
    throw new IllegalArgumentException("athenz authParams needs 'privateKey' or 'privateKeyPath'");
}
if (key.startsWith("file:")) {
    if (!Files.isReadable(Paths.get(URI.create(key)))) throw new IllegalArgumentException("key file missing/unreadable");
}

Type guard

boolean hasPrivateKey(Map<String,String> p) {
    return p.containsKey("privateKey") || p.containsKey("privateKeyPath");
}

Try / catch

try {
    authentication.configure(authParamsJson);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("Failed to load private key")) {
        log.error("Check 'privateKey'/'privateKeyPath' in authParams and that the key file exists");
    }
    throw e;
}

Prevention

When it happens

Trigger: configure() called with JSON missing both 'privateKey' and 'privateKeyPath' entries, or with values that loadPrivateKey cannot resolve (bad URL scheme, missing file, unparseable PEM).

Common situations: Athenz config copied from docs with placeholder paths; key file moved or deleted; typos like 'privatekey' or 'private_key' in the JSON keys; key data URI missing the data: prefix.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/82f0e5136c134ca7. Report an issue: GitHub.