passbolt/passbolt_api · error · CustomValidationException
A valid OpenPGP key must be provided.
Error message
A valid OpenPGP key must be provided.
What it means
CustomValidationException with message 'A valid OpenPGP key must be provided.' thrown when isParsableArmoredPublicKey fails — the armored string cannot be parsed as an OpenPGP key at all. This mandatory check always runs first in parseAndValidatePublicKey; other rules are skipped when it fails.
Solutions
- Provide a complete, valid OpenPGP public key ASCII armor including the BEGIN/END PGP PUBLIC KEY BLOCK lines.
- Pre-validate with PublicKeyValidationService::isParsableArmoredPublicKey before submitting to give a friendlier error.
- Ensure the client sends the key as a plain string without HTML-escaping or base64 wrapping.
- Catch CustomValidationException to read the structured validation errors (armored_key.IS_PARSABLE_ARMORED_KEY_RULE).
Example fix
// before
$this->assertMetadataKey($armoredKey); // throws on bad paste
// after
if (!PublicKeyValidationService::isParsableArmoredPublicKey($armoredKey)) {
throw new BadRequestException(__('Please paste a complete OpenPGP public key block starting with -----BEGIN PGP PUBLIC KEY BLOCK-----'));
}
$this->assertMetadataKey($armoredKey); Defensive patterns
Strategy: validation
Validate before calling
// PHP
if (!PublicKeyValidationService::isParsableArmoredPublicKey($armoredKey)) {
throw new BadRequestException(__('Provide a complete OpenPGP public key armored block.'));
} Type guard
function looksLikeArmoredPublicKey(?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 {
PublicKeyValidationService::parseAndValidatePublicKey($armoredKey);
} catch (\App\Error\Exception\CustomValidationException $e) {
return $this->respondWithError(400, __('The provided key is not a parsable OpenPGP key.'));
} Prevention
- Pre-check armor headers client-side before submitting the key.
- Strip whitespace/BOM and ensure no HTML escaping of the key payload.
- Instruct users to export the PUBLIC key, never the private one.
When it happens
Trigger: Any call to parseAndValidatePublicKey (from assertMetadataKey, buildPublicKeyEntityFromDataOrFail, buildRevokedKeyEntityFromDataOrFail, create, rule, canValidate) with input missing the PGP armored block headers, malformed ASCII armor, or binary garbage.
Common situations: Users pasting a private key, a plain text file, or a truncated key; missing BEGIN PGP PUBLIC KEY BLOCK header due to copy/paste; client sending an empty string.
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
- A valid OpenPGP key must be provided.
- Could not save the account recovery private key.
- Could not validate key revocation.
- Could not validate message data.
- Could not validate metadata key data.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/afdee6429923340b.
Report an issue: GitHub.
Appendix: source
Thrown at src/Service/OpenPGP/PublicKeyValidationService.php:154
* @param string $armoredKey user provided data
* @param array|null $rules to override default rules
* @throws \App\Error\Exception\CustomValidationException If parsing public key fails
* @throws \App\Error\Exception\CustomValidationException Validation rules fails
* @throws \Cake\Http\Exception\InternalErrorException No public key validation rules
* @throws \Cake\Http\Exception\InternalErrorException Unknown key validation rule
* @return array key information (see OpenPGPBackendInterface::getKeyInfo)
*/
public static function parseAndValidatePublicKey(string $armoredKey, ?array $rules = null): array
{
$rules = $rules ?? self::getDefaultRules();
if (!count($rules)) {
throw new InternalErrorException('Invalid public key validation rules are missing.');
}
// Parsing check is mandatory and always done first
// We don't even try the other rules if this one fails
if (!self::isParsableArmoredPublicKey($armoredKey)) {
throw new CustomValidationException(__('A valid OpenPGP key must be provided.'), [
'armored_key' => [
self::IS_PARSABLE_ARMORED_KEY_RULE => __('The public key could not be parsed.'),
],
]);
}
// Other rules are recommended but not mandatory
// As one may want to see what's inside the key info for debugging purpose
$keyInfo = self::getPublicKeyInfo($armoredKey);
$validationErrors = [];
foreach ($rules as $ruleName) {
switch ($ruleName) {
case self::IS_VALID_ALGORITHM_RULE:
if (!self::isValidAlgorithm($keyInfo['type'], false)) {
$validationErrors[$ruleName] = __('The algorithm is invalid.');
}
break;
case self::IS_VALID_ALGORITHM_STRICT_RULE:View on GitHub (pinned to 31c1bbc10f)