passbolt/passbolt_api · error · InvalidJwtKeyPairException

The JWT private key could not be written.

Error message

The JWT private key could not be written.

What it means

After openssl_pkey_new succeeds, createKeyPair() exports the private key to the secret key path via openssl_pkey_export_to_file(). When that call returns false (export failure, usually a filesystem permission problem on the target directory), this error is thrown and rethrown as InvalidJwtKeyPairException.

Solutions

  1. Create the JWT directory and give the web user ownership: mkdir -p config/jwt && chown www-data:www-data config/jwt
  2. Check free disk space and that the path is on a writable mount (df -h; mount)
  3. Verify no passphrase/openssl config error: capture openssl error with openssl_error_string() right after the failure
  4. Re-run the key pair generation command as the user that owns config/jwt

Example fix

// before
$export = openssl_pkey_export_to_file($pk, $secretKeyPath); // dir missing
// after
if (!is_dir(dirname($secretKeyPath))) {
    mkdir(dirname($secretKeyPath), 0770, true);
}
$export = openssl_pkey_export_to_file($pk, $secretKeyPath);
Defensive patterns

Strategy: validation

Validate before calling

$dir = dirname($secretKeyPath);
if (!is_dir($dir) || !is_writable($dir)) { mkdir($dir, 0770, true); }

Try / catch

try { $service->createKeyPair(); } catch (InvalidJwtKeyPairException $e) { // check is_writable(config/jwt) and current process user }

Prevention

When it happens

Trigger: openssl_pkey_export_to_file($pk, $secretKeyPath) returns false — the directory at getSecretKeyPath() (config/jwt/) does not exist, is not writable by the current user (e.g. www-data vs CLI user mismatch), or an OpenSSL passphrase/config issue blocks export.

Common situations: config/jwt/ not created before running the command; running `passbolt create jwt_keys` as root then the web server cannot continue; read-only or immutable mount; SELinux/AppArmor blocking writes.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/0dfee4c227684672. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltCe/JwtAuthentication/src/Service/AccessToken/JwtKeyPairService.php:78

            return;
        }

        $config = [
            'digest_alg' => JwtTokenCreateService::JWT_ALG,
            'private_key_bits' => $this->getKeyLength(),
            'private_key_type' => OPENSSL_KEYTYPE_RSA,
        ];
        $secretKeyPath = $this->getSecretKeyPath();
        $publicKeyPath = $this->getPublicKeyPath();

        try {
            $pk = openssl_pkey_new($config);
            if ($pk === false) {
                throw new Exception('The JWT private key could not be created.');
            }
            $export = openssl_pkey_export_to_file($pk, $secretKeyPath);
            if ($export === false) {
                throw new Exception('The JWT private key could not be written.');
            }
            $publicKey = openssl_pkey_get_details($pk)['key'] ?? false;
            if ($publicKey === false) {
                throw new Exception('The JWT public key could not be extracted.');
            }
            $export = file_put_contents($publicKeyPath, $publicKey);
            if ($export === false) {
                throw new Exception('The JWT public key could not be written.');
            }

            $permission = 0640;
            $res = chmod($secretKeyPath, $permission);
            if (!$res) {
                throw new Exception("The permission of $secretKeyPath could not be set to $permission.");
            }
            $res = chmod($publicKeyPath, $permission);
            if (!$res) {
                throw new Exception("The permission of $publicKeyPath could not be set to $permission.");

View on GitHub (pinned to 31c1bbc10f)