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

  1. Include a valid armored public key in the request body: {"gpgkey": {"armored_key": "-----BEGIN PGP PUBLIC KEY BLOCK-----..."}}
  2. Check the request Content-Type is application/json and the key is a plain string, not nested differently
  3. Generate/export the user key first (e.g. via openpgp) before calling the complete endpoint
  4. 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

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


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)