passbolt/passbolt_api · error · ValidationException

The OpenPGP key data is not valid.

Error message

The OpenPGP key data is not valid.

What it means

During setup/recover completion, the submitted OpenPGP public key passed syntax parsing but failed the Gpgkeys table business rules (checkRules), so a ValidationException is thrown carrying the entity errors. Passbolt throws this to prevent persisting a key that would break encryption for the account.

Solutions

  1. Regenerate the key with GnuPG (modern algo, not expired/revoked) and retry setup with the fresh armored key
  2. Inspect the `errors` payload of the 400 response to see the exact failing Gpgkeys field
  3. Ensure the armored key corresponds to the fingerprint recorded when the registration token was issued
  4. Check the key has a single key block, valid user ID, and no future/invalid creation date

Example fix

// before: posting a mismatched key during setup
{ "gpgkey": { "armored_key": "-----BEGIN PGP PUBLIC KEY BLOCK----- ...other-identity-key..." } }
// after: use the key pair generated for this account
{ "gpgkey": { "armored_key": "-----BEGIN PGP PUBLIC KEY BLOCK----- ...account-key..." } }
Defensive patterns

Strategy: validation

Validate before calling

use App\Service\OpenPGP\PublicKeyValidationService;
PublicKeyValidationService::parseAndValidatePublicKey($armoredKey, PublicKeyValidationService::getStrictRules());

Try / catch

try {
    $user = $setupCompleteService->complete($userId);
} catch (\App\Error\Exception\ValidationException $e) {
    $fieldErrors = $e->getErrors(); // inspect gpgkey field errors
}

Prevention

When it happens

Trigger: POST to /setup/complete (or /recover/complete) with an armored key that violates model rules: invalid fingerprint checksum, mismatched key info versus armored key, duplicate fingerprint already registered, or missing/invalid key fields.

Common situations: User imports a different key than the one whose fingerprint is registered; key generated by non-compliant tooling; duplicate setup attempt with an already-stored key; truncated or edited armored key blocks.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at src/Service/Setup/SetupCompleteService.php:87

    {
        // Check request sanity
        $user = $this->getAndAssertUser($userId);
        $token = $this->getAndAssertToken($userId, AuthenticationToken::TYPE_REGISTER);
        $gpgkey = $this->getAndAssertGpgkey($userId);

        // New with 3.6 - Check armored key content
        // The key must not be expired or revoked, or have multiple key blocks, etc.
        // TODO w4.0 - Move to getAndAssertGpgkey
        //  Will break compat on recover for non compliant keys
        PublicKeyValidationService::parseAndValidatePublicKey(
            $gpgkey->armored_key,
            PublicKeyValidationService::getStrictRules()
        );

        // Check business rules before saving
        $this->Gpgkeys->checkRules($gpgkey);
        if ($gpgkey->getErrors()) {
            throw new ValidationException(__('The OpenPGP key data is not valid.'), $gpgkey, $this->Gpgkeys);
        }

        // Check key can be used to encrypt
        // This can happen for example if the key is created in the future
        // or some other issue prevent the backend to use it, we don't want to fail at the login step
        if (Configure::read('passbolt.gpg.experimental.encryptValidate')) {
            if (!PublicKeyCanEncryptCheckService::check($gpgkey->armored_key, $gpgkey->fingerprint)) {
                $msg = __('The OpenPGP key can not be used to encrypt.');
                Log::debug($msg, [$gpgkey->armored_key]);
                throw new CustomValidationException($msg, ['gpgkey' => ['armored_key' => $msg]]);
            }
        }

        // Consume atomically before the user save so a losing concurrent request never reaches persistence.
        $this->consumeTokenOrFail($token);

        $user->active = true;
        $user->gpgkey = $gpgkey;

View on GitHub (pinned to 31c1bbc10f)