passbolt/passbolt_api · error · BadRequestException
An OpenPGP key must be provided.
Error message
An OpenPGP key must be provided.
What it means
Thrown by getAndAssertGpgkey when the request payload has no usable OpenPGP armored key at data['gpgkey']['armored_key']. Completing setup requires publishing the user's public key so the server can encrypt secrets to them; without it setup cannot finish.
Solutions
- Include a valid armored public key in the request body: {"gpgkey": {"armored_key": "-----BEGIN PGP PUBLIC KEY BLOCK-----..."}}
- Check the request Content-Type is application/json and the key is a plain string, not nested differently
- Generate/export the user key first (e.g. via openpgp) before calling the complete endpoint
- Verify the client library version still matches the API payload contract
Example fix
// before
await fetch(url, {method:'POST', body: JSON.stringify({authenticationtoken: {token}})});
// after
await fetch(url, {method:'POST', body: JSON.stringify({authenticationtoken: {token}, gpgkey: {armored_key: publicKeyArmor}})}); Defensive patterns
Strategy: validation
Validate before calling
const armored = body?.gpgkey?.armored_key;
if (typeof armored !== 'string' || !armored.startsWith('-----BEGIN PGP PUBLIC KEY BLOCK-----')) throw new Error('armored key required'); Type guard
function hasArmoredKey(d): d is {gpgkey:{armored_key:string}} { return typeof d?.gpgkey?.armored_key === 'string' && d.gpgkey.armored_key.length > 0; } Try / catch
try { await completeSetup(userId, token, payload); }
catch (e) { if (isBadRequestMissingGpgkey(e)) { /* prompt user to attach key */ } } Prevention
- Always build the payload with gpgkey.armored_key before calling setup/complete
- Send Content-Type: application/json
- Export the public key as a plain string, not a parsed object
- Validate the armor block client-side before the request
When it happens
Trigger: POST /setup/complete/{userId}/{tokenId} with a body missing the gpgkey.armored_key field, sending null, or sending a non-string (e.g. a parsed object); sending an empty string.
Common situations: Client forgot to include the key in the JSON body; key serialized under a wrong field name; content-type issue so the body is not parsed; front-end sends an object instead of the armored string.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- The OpenPGP key can not be used to encrypt.
- The OpenPGP key data is not valid.
- A valid OpenPGP key must be provided.
- A valid OpenPGP key must be provided.
- A value for the theme should be provided.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/943e75591f170635.
Report an issue: GitHub.
Appendix: source
Thrown at src/Service/Setup/AbstractCompleteService.php:145
$token->setDirty('active', false);
}
/**
* Return the gpg key entity for matching the requesting id
*
* @param string $userId the user uuid
* @throws \Cake\Http\Exception\BadRequestException if the gpg key is not provided or not a valid OpenPGP key
* @throws \App\Error\Exception\CustomValidationException if armored key content cannot be validated
* @throws \App\Error\Exception\ValidationException if key cannot be validated against model rules
* @return \App\Model\Entity\Gpgkey entity
*/
protected function getAndAssertGpgkey(string $userId): Gpgkey
{
$data = $this->request->getData();
$armoredKey = $data['gpgkey']['armored_key'] ?? null;
if (empty($armoredKey) || !is_string($armoredKey)) {
throw new BadRequestException(__('An OpenPGP key must be provided.'));
}
try {
return $this->Gpgkeys->buildEntityFromArmoredKey($armoredKey, $userId);
} catch (ValidationException $exception) {
// Remap errors to match sent data
throw new CustomValidationException($exception->getMessage(), ['gpgkey' => $exception->getErrors()]);
}
}
}
View on GitHub (pinned to 31c1bbc10f)