passbolt/passbolt_api · error · App\Error\Exception\ValidationException
The OpenPGP armored key could not be validated.
Error message
The OpenPGP armored key could not be validated.
What it means
After building the Gpgkey entity, buildEntityFromArmoredKey() runs it through entity validation ($this->newEntity/build with rules). If the entity has errors (e.g. key not expired, valid email match, uid mismatch), it throws a ValidationException with this message carrying the entity errors.
Solutions
- Inspect the ValidationException errors property to see which rule failed (expires, uid, key_created, etc.).
- Have the user generate/export a fresh non-expired key whose uid email matches their account.
- Check server clock synchronization if key_created errors appear.
- Fix client-side pre-validation to match the entity validation rules (expires, deleted flags).
Example fix
// before
$gpgkey = $this->Gpgkeys->buildEntityFromArmoredKey($armoredKey, $userId); // ValidationException
// after
try { $gpgkey = $this->Gpgkeys->buildEntityFromArmoredKey($armoredKey, $userId); }
catch (ValidationException $e) { $details = $e->getErrors(); // inspect which rule failed
throw new BadRequestException('GPG key rejected: ' . json_encode($details)); } Defensive patterns
Strategy: try-catch
Validate before calling
$info = PublicKeyValidationService::getPublicKeyInfo($armoredKey);
if (isset($info['expires']) && $info['expires'] !== null && $info['expires'] < time()) {
throw new BadRequestException('The provided OpenPGP key is expired.');
}
if (!in_array($userEmail, array_column($info['uids'] ?? [], 'email'), true)) {
throw new BadRequestException('Key uid email must match the user email.');
} Try / catch
try { $entity = $gpgkeysTable->buildEntityFromArmoredKey($armoredKey, $userId); }
catch (ValidationException $e) {
$errors = $e->getErrors(); // field-level detail (expires, uid, key_created...)
throw new BadRequestException(json_encode($errors));
} Prevention
- Check key expiry and revocation before submission.
- Ensure the key uid email equals the account email.
- Keep the server clock NTP-synchronized to avoid key_created skew.
- Surface the entity errors array to users instead of the generic message.
When it happens
Trigger: The armored key parses but fails field validation — key is expired, revoked, key_created in the future, uid/email does not match the user's email, or key type/algorithm not accepted.
Common situations: Users importing expired or revoked keys, keys generated with an email different from their account, or system clock skew making key_created appear in the future.
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
- A valid OpenPGP key must be provided.
- Could not validate message data.
- Could not validate password data.
- Could not validate user data.
- Invalid request, message validation rules are missing.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/e4e28e5ed95152e1.
Report an issue: GitHub.
Appendix: source
Thrown at src/Model/Table/GpgkeysTable.php:319
if (!empty($info['expires'])) {
$data['expires'] = new DateTime($info['expires']);
}
$gpgKey = $this->newEntity($data, ['accessibleFields' => [
'user_id' => true,
'fingerprint' => true,
'bits' => true,
'type' => true,
'key_id' => true,
'uid' => true,
'armored_key' => true,
'key_created' => true,
'deleted' => true,
'expires' => true,
]]);
if ($gpgKey->getErrors()) {
throw new ValidationException(__('The OpenPGP armored key could not be validated.'), $gpgKey, $this);
}
return $gpgKey;
}
/**
* Custom validation rule to validate key id
*
* @param string $value fingerprint
* @param array|null $context not in use
* @return bool
* @deprecated Use PublicKeyValidationService::isParsableArmoredPublicKey
*/
public function isParsableArmoredPublicKey(string $value, ?array $context = null): bool
{
return PublicKeyValidationService::isParsableArmoredPublicKey($value);
}
View on GitHub (pinned to 31c1bbc10f)