passbolt/passbolt_api · error · InternalErrorException

No OpenPGP key found for the user. The metadata could not…

Error message

No OpenPGP key found for the user. The metadata could not be encrypted with the user id: {0}.

What it means

Personal tag metadata is encrypted with the owning user's OpenPGP public key. If the user entity has no gpgkey loaded or the user genuinely has no key, the service throws InternalErrorException combining 'No OpenPGP key found for the user.' with the user id context.

Solutions

  1. Add the Gpgkeys containment to the migration query: contain(['Users' => ['Gpgkeys']]).
  2. Have the affected user complete OpenPGP key setup, then rerun migration.
  3. Delete or reassign the tag if the owner is a provisioned account that will never have a key.
  4. Pre-check users without keys and exclude their tags from the batch.

Example fix

// before
$tags = $this->Tags->find()->contain(['Users'])->all();
// after
$tags = $this->Tags->find()->contain(['Users' => ['Gpgkeys']])->all();
Defensive patterns

Strategy: validation

Validate before calling

$keyless = $usersTable->find()
    ->leftJoinWith('Gpgkeys')
    ->where(['Gpgkeys.id IS NULL'])
    ->all();
// exclude tags owned by $keyless users from the migration batch

Type guard

function hasGpgKey(User $user): bool {
    return $user->gpgkey !== null && $user->gpgkey->fingerprint !== null;
}

Try / catch

try {
    $service->migrate($uac, $batch);
} catch (InternalErrorException $e) {
    if (str_contains($e->getMessage(), 'No OpenPGP key found')) {
        $this->log('Owner lacks GPG key; defer tag migration');
    } else { throw $e; }
}

Prevention

When it happens

Trigger: Migrating a personal tag whose owner has no OpenPGP key registered (user never completed key setup) or where the migration query did not contain the user's gpgkey association.

Common situations: Legacy users created via LDAP/SCIM provisioning without completing passbolt key setup; migration batch query missing contain(['Users' => ['Gpgkeys']]); suspended/deleted accounts owning active tags.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/78d1a2aed8d9ef1e. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltEe/Tags/src/Service/Metadata/MigrateAllV4TagsToV5Service.php:211

     */
    private function migratePersonal(MetadataTagDto $dto, Tag $tag): void
    {
        $metadataArray = $dto->getClearTextMetadata();
        $users = $tag->get('users');

        if (!is_array($users) || count($users) < 1) {
            throw new InternalErrorException(__('No user found for the personal tag id: "{0}".', $tag->id));
        }

        /** @var \App\Model\Entity\User $user */
        $user = $users[0];
        if (is_null($user)) {
            throw new InternalErrorException(__('User contain data missing for the tag id: "{0}".', $tag->id));
        }
        if (is_null($user->gpgkey)) {
            $msg = __('No OpenPGP key found for the user.') . ' ';
            $msg .= __('The metadata could not be encrypted with the user id: {0}.', $user->id);
            throw new InternalErrorException($msg);
        }

        try {
            $gpg = OpenPGPBackendFactory::get();
            $gpg->clearKeys();
            $gpg = $this->setSignKeyWithServerKey($gpg);
            $gpg = $this->setEncryptKeyWithUserKey($gpg, $user->gpgkey);
            $metadataClearText = json_encode($metadataArray, JSON_THROW_ON_ERROR);
            $metadataEncrypted = $gpg->encrypt($metadataClearText, true);
        } catch (Exception $exception) {
            $msg = $exception->getMessage() . ' ';
            $msg .= __('The metadata could not be encrypted with the user id: {0}.', $user->id);
            throw new InternalErrorException($msg, 500, $exception);
        }

        $this->updateTag($tag, [
            'slug' => null,
            'metadata' => $metadataEncrypted,

View on GitHub (pinned to 31c1bbc10f)