passbolt/passbolt_api · error · InvalidJwtKeyPairException
The JWT public key could not be written.
Error message
The JWT public key could not be written.
What it means
createKeyPair() writes the extracted public key PEM to the public key path with file_put_contents(). A false return triggers this error, wrapped in InvalidJwtKeyPairException. It means the public key file could not be created or overwritten at getPublicKeyPath().
Solutions
- Ensure config/jwt exists and is writable by the runtime user: chown -R www-data:www-data config/jwt
- Check the public key path is a regular writable file, not a directory (ls -la config/jwt)
- Verify disk space and mount writability (df -h; touch config/jwt/test)
- If the container FS is read-only, generate the keys at build time or mount config/jwt as a writable volume
Example fix
// before
$export = file_put_contents($publicKeyPath, $publicKey);
// after
if (!is_writable(dirname($publicKeyPath))) {
chmod(dirname($publicKeyPath), 0770); // or chown to runtime user
}
$export = file_put_contents($publicKeyPath, $publicKey); Defensive patterns
Strategy: validation
Validate before calling
if (!is_dir($dir)) { mkdir($dir, 0770, true); }
if (!is_writable($dir)) { throw new \RuntimeException("{$dir} not writable by " . get_current_user()); } Try / catch
try { $service->createKeyPair(); } catch (InvalidJwtKeyPairException $e) { // chown/chmod config/jwt to the runtime user, then retry } Prevention
- Provision config/jwt as a writable volume before first run
- Match CLI and web-server users for key management
- Add writability checks to deployment health checks
When it happens
Trigger: file_put_contents($publicKeyPath, $publicKey) returns false because config/jwt/ is missing, not writable by the current process, the path exists as a directory, or the filesystem is full/read-only.
Common situations: Web-server user (www-data) differs from CLI user that created config/jwt; SELinux contexts; deploying to immutable containers where config/ is read-only at runtime.
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
- The JWT private key could not be written.
- The JWT public key could not be read or is not valid.
- The key pair for JWT Authentication is not complete.
- The permission of $publicKeyPath could not be set to…
- The permission of $secretKeyPath could not be set to…
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/3d37df147c78ee1b.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/JwtAuthentication/src/Service/AccessToken/JwtKeyPairService.php:86
$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.");
}
} catch (Throwable $e) {
throw new InvalidJwtKeyPairException($e->getMessage());
}
}
/**
* Validate the key pair validity as defined by the public and secret services.View on GitHub (pinned to 31c1bbc10f)