passbolt/passbolt_api · error · Cake\Http\Exception\BadRequestException
The user OpenPGP key does not exist, or is invalid, or has…
Error message
The user OpenPGP key does not exist, or is invalid, or has been deleted.
What it means
assertUserData checks the loaded user has an associated gpgkey with a valid fingerprint and armored_key. BadRequestException (400) is thrown when the user record exists but their OpenPGP key is missing, malformed, or the fingerprint fails PublicKeyValidationService validation.
Solutions
- Have the user upload a valid OpenPGP key via the passbolt UI or the /users/<id>/gpgkey endpoint
- Verify the gpgkeys table row exists for that user_id with non-empty armored_key and fingerprint
- Re-import the user key (e.g. using passbolt CLI or send the key again during account setup)
- Check the stored fingerprint is a valid 40-char hex fingerprint; fix via key re-upload
Example fix
// before: user created without key $users->save(new User(['username' => 'x', ...])); // no gpgkey // after $user = $users->register(...); $gpgkeys->importForUser($user, $armoredKey);
Defensive patterns
Strategy: validation
Validate before calling
if (!user.gpgkey || !user.gpgkey.armored_key || !/^[0-9A-F]{40}$/.test(user.gpgkey.fingerprint || '')) throw new Error('user has no valid OpenPGP key'); Type guard
function hasValidGpgKey(u) { return Boolean(u && u.gpgkey && typeof u.gpgkey.armored_key === 'string' && typeof u.gpgkey.fingerprint === 'string' && /^[0-9A-F]{40}$/.test(u.gpgkey.fingerprint)); } Try / catch
try { await login(); } catch (e) { if (/OpenPGP key does not exist/.test(e.message)) { await promptUserToUploadKey(); } } Prevention
- Complete the key upload step during account setup before attempting JWT login
- Avoid deleting the gpgkey during rotations without uploading a replacement
- Check the Users GET response contains gpgkey before login
- Back up gpgkeys table with users on restores
When it happens
Trigger: POST /auth/jwt/login for a user whose gpgkeys row is absent (key never uploaded or deleted), whose armored_key/fingerprint columns are not strings, or whose stored fingerprint is invalid (wrong length/charset).
Common situations: User account created via API/CLI without uploading a GPG key; key deleted from profile during key rotation; database restore that lost gpgkeys rows; corrupted armored key from a bad import.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- Could not import the user OpenPGP key.
- Could not validate user data.
- A valid OpenPGP key must be provided.
- Could not import the user OpenPGP key.
- Could not validate message data.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/5bf6d9f6ee9c0753.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/JwtAuthentication/src/Authenticator/GpgJwtAuthenticator.php:408
}
/**
* @param mixed $userData data
* @throws \Cake\Http\Exception\BadRequestException
* @return void
*/
public function assertUserData(mixed $userData): void
{
if (
!isset($userData->gpgkey) ||
!isset($userData->gpgkey->fingerprint) ||
!isset($userData->gpgkey->armored_key) ||
!is_string($userData->gpgkey->fingerprint) ||
!PublicKeyValidationService::isValidFingerprint($userData->gpgkey->fingerprint) ||
!is_string($userData->gpgkey->armored_key)
) {
$msg = __('The user OpenPGP key does not exist, or is invalid, or has been deleted.');
throw new BadRequestException($msg);
}
}
/**
* @param mixed $armoredChallenge challenge
* @throws \InvalidArgumentException if armored challenge is invalid
* @return void
*/
public function assertArmoredChallenge(mixed $armoredChallenge): void
{
$this->assertGpgMessageIsValid($this->gpg, $armoredChallenge, __('The user challenge is missing or invalid.'));
}
/**
* @param mixed $version version
* @throws \Exception if version is not supported
* @return void
*/View on GitHub (pinned to 31c1bbc10f)