{"record":{"id":"5bf6d9f6ee9c0753","repo":"passbolt/passbolt_api","slug":"the-user-openpgp-key-does-not-exist-or-is-invalid-or-has","errorCode":null,"errorMessage":"The user OpenPGP key does not exist, or is invalid, or has been deleted.","messagePattern":"The user OpenPGP key does not exist, or is invalid, or has been deleted\\.","errorType":"exception","errorClass":"Cake\\Http\\Exception\\BadRequestException","httpStatus":400,"severity":"error","filePath":"plugins/PassboltCe/JwtAuthentication/src/Authenticator/GpgJwtAuthenticator.php","lineNumber":408,"sourceCode":"    }\n\n    /**\n     * @param mixed $userData data\n     * @throws \\Cake\\Http\\Exception\\BadRequestException\n     * @return void\n     */\n    public function assertUserData(mixed $userData): void\n    {\n        if (\n            !isset($userData->gpgkey) ||\n            !isset($userData->gpgkey->fingerprint) ||\n            !isset($userData->gpgkey->armored_key) ||\n            !is_string($userData->gpgkey->fingerprint) ||\n            !PublicKeyValidationService::isValidFingerprint($userData->gpgkey->fingerprint) ||\n            !is_string($userData->gpgkey->armored_key)\n        ) {\n            $msg = __('The user OpenPGP key does not exist, or is invalid, or has been deleted.');\n            throw new BadRequestException($msg);\n        }\n    }\n\n    /**\n     * @param mixed $armoredChallenge challenge\n     * @throws \\InvalidArgumentException if armored challenge is invalid\n     * @return void\n     */\n    public function assertArmoredChallenge(mixed $armoredChallenge): void\n    {\n        $this->assertGpgMessageIsValid($this->gpg, $armoredChallenge, __('The user challenge is missing or invalid.'));\n    }\n\n    /**\n     * @param mixed $version version\n     * @throws \\Exception if version is not supported\n     * @return void\n     */","sourceCodeStart":390,"sourceCodeEnd":426,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltCe/JwtAuthentication/src/Authenticator/GpgJwtAuthenticator.php#L390-L426","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before: user created without key\n$users->save(new User(['username' => 'x', ...])); // no gpgkey\n// after\n$user = $users->register(...);\n$gpgkeys->importForUser($user, $armoredKey);","handlingStrategy":"validation","validationCode":"if (!user.gpgkey || !user.gpgkey.armored_key || !/^[0-9A-F]{40}$/.test(user.gpgkey.fingerprint || '')) throw new Error('user has no valid OpenPGP key');","typeGuard":"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)); }","tryCatchPattern":"try { await login(); } catch (e) { if (/OpenPGP key does not exist/.test(e.message)) { await promptUserToUploadKey(); } }","preventionTips":["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"],"tags":["gpg","user-key","validation","jwt"],"backgroundTag":"entity-not-found","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}