passbolt/passbolt_api · error · App\Error\Exception\CustomValidationException
A valid OpenPGP key must be provided.
Error message
A valid OpenPGP key must be provided.
What it means
GpgkeysTable::buildEntityFromArmoredKey() parses the armored key with PublicKeyValidationService::getPublicKeyInfo(). Any parsing exception is converted to a CustomValidationException stating a valid OpenPGP key must be provided, with an armored_key.isParsable error detail.
Solutions
- Validate the armored key client-side/server-side with PublicKeyValidationService::getPublicKeyInfo() before calling buildEntityFromArmoredKey().
- Ensure the key includes full ASCII armor headers ('-----BEGIN PGP PUBLIC KEY BLOCK-----').
- Re-export the key with gpg --armor --export <fingerprint>.
- Catch the CustomValidationException and return the field error to the user.
Example fix
// before
$gpgkey = $this->Gpgkeys->buildEntityFromArmoredKey($data['armored_key'], $userId); // throws
// after
try { PublicKeyValidationService::getPublicKeyInfo($data['armored_key']); }
catch (Exception $e) { throw new BadRequestException('Please provide a valid OpenPGP public key.'); }
$gpgkey = $this->Gpgkeys->buildEntityFromArmoredKey($data['armored_key'], $userId); Defensive patterns
Strategy: validation
Validate before calling
use App\Service\OpenPGP\PublicKeyValidationService;
try {
$info = PublicKeyValidationService::getPublicKeyInfo($armoredKey);
} catch (Exception $e) {
throw new BadRequestException('Provide a valid OpenPGP armored public key.');
} Type guard
function looksLikeArmoredKey(?string $key): bool {
return is_string($key)
&& str_contains($key, '-----BEGIN PGP PUBLIC KEY BLOCK-----')
&& str_contains($key, '-----END PGP PUBLIC KEY BLOCK-----');
} Try / catch
try { $entity = $gpgkeysTable->buildEntityFromArmoredKey($armoredKey, $userId); }
catch (CustomValidationException $e) {
return $this->response->withStatus(400)->withStringBody(json_encode($e->getErrors()));
} Prevention
- Verify armor BEGIN/END lines survive copy/paste (whitespace trimming).
- Accept only PGP public key blocks, not SSH keys or certificates.
- Pre-validate keys with PublicKeyValidationService before persistence.
- Provide clear UI feedback pointing users to gpg --armor --export.
When it happens
Trigger: Calling buildEntityFromArmoredKey() with a string that is not a parseable armored OpenPGP key — wrong armor format, truncated key, non-key data, or corrupt ASCII armor.
Common situations: Users pasting SSH keys or certificates instead of PGP keys, copy/paste losing the BEGIN/END armor lines, keys generated by incompatible tooling, or wrong encoding.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Could not validate message data.
- Could not validate user data.
- Invalid request, message validation rules are missing.
- No OpenPGP marker found.
- The armored message could not be validated.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/b2656128ba67c4de.
Report an issue: GitHub.
Appendix: source
Thrown at src/Model/Table/GpgkeysTable.php:281
/**
* Build a Gpgkey entity from the armored key
*
* @param string $armoredKey ascii armored key
* @param string $userId uuid of the user using the key
* @throws \InvalidArgumentException if the user is not valid
* @throws \App\Error\Exception\ValidationException if the key info can not be parsed
* @return \App\Model\Entity\Gpgkey
*/
public function buildEntityFromArmoredKey(string $armoredKey, string $userId): Gpgkey
{
if (!Validation::uuid($userId)) {
throw new InvalidArgumentException('The user identifier should be a valid UUID.');
}
try {
$info = PublicKeyValidationService::getPublicKeyInfo($armoredKey);
} catch (Exception $e) {
throw new CustomValidationException(__('A valid OpenPGP key must be provided.'), [
'armored_key' => [
'isParsable' => __('The OpenPGP armored key could not be parsed.'),
],
]);
}
$data = [
'user_id' => $userId,
'fingerprint' => $info['fingerprint'],
'bits' => $info['bits'],
'type' => $info['type'],
'key_id' => $info['key_id'],
'uid' => $info['uid'],
'armored_key' => $armoredKey,
'deleted' => false,
'key_created' => new DateTime($info['key_created']),
'expires' => null,
];View on GitHub (pinned to 31c1bbc10f)