passbolt/passbolt_api · error · InternalErrorException

Could not validate user data.

Error message

Could not validate user data.

What it means

Before using a user OpenPGP key to encrypt, setEncryptKeyWithUserKey() calls assertUserKey(), which requires the Gpgkey entity to have a non-empty armored_key, a valid fingerprint string, and an armored key that parses as a valid public key. If any of those assertions fail, the underlying InternalErrorException is re-thrown as 'Could not validate user data.' with HTTP 500. It indicates corrupted or incomplete user key data, not a keyring problem.

Solutions

  1. Inspect the Gpgkey entity for the affected user (gpgkeys table) and verify both fingerprint and armored_key are populated and consistent with each other.
  2. Re-import or re-save the user's public key so validation rules run, or have the user re-upload their key if the stored key is corrupted.
  3. Ensure callers pass a fully hydrated App\Model\Entity\Gpgkey (not an array or partial object) into setEncryptKeyWithUserKey.
  4. Check the chained exception (previous) in logs — the original assertUserKey message tells which field failed ('not available or incomplete').
  5. Run bin/cake passbolt healthcheck (key checks) to find users with invalid key records.

Example fix

// before
$gpg = $this->setEncryptKeyWithUserKey($gpg, $user->gpgkey); // may be null/partial
// after
if ($user->gpgkey === null
    || !isset($user->gpgkey->armored_key, $user->gpgkey->fingerprint)
    || !PublicKeyValidationService::isValidFingerprint($user->gpgkey->fingerprint)) {
    throw new BadRequestException(__('The user key data is incomplete.'));
}
$gpg = $this->setEncryptKeyWithUserKey($gpg, $user->gpgkey);
Defensive patterns

Strategy: validation

Validate before calling

use App\Service\OpenPGP\PublicKeyValidationService;

function canUseUserKeyForEncryption(\App\Model\Entity\Gpgkey $userKey): bool
{
    return isset($userKey->armored_key, $userKey->fingerprint)
        && is_string($userKey->fingerprint)
        && PublicKeyValidationService::isValidFingerprint($userKey->fingerprint)
        && is_string($userKey->armored_key)
        && PublicKeyValidationService::parseAndValidatePublicKey($userKey->armored_key);
}

Type guard

function isUsableGpgkey(mixed $key): bool
{
    return $key instanceof \App\Model\Entity\Gpgkey
        && isset($key->armored_key, $key->fingerprint)
        && is_string($key->armored_key)
        && is_string($key->fingerprint);
}

Try / catch

try {
    $gpg = $this->setEncryptKeyWithUserKey($gpg, $userKey);
} catch (InternalErrorException $e) {
    // inspect $e->getPrevious() for the exact assertUserKey failure
    error_log('User key validation failed: ' . ($e->getPrevious()?->getMessage() ?? $e->getMessage()));
    throw new BadRequestException('The user OpenPGP key data is incomplete or invalid.');
}

Prevention

When it happens

Trigger: Calling setEncryptKeyWithUserKey($gpg, $userKey) where $userKey is a Gpgkey entity whose armored_key or fingerprint is null/unset, whose fingerprint fails PublicKeyValidationService::isValidFingerprint(), or whose armored_key fails PublicKeyValidationService::parseAndValidatePublicKey().

Common situations: Data seeded by scripts or fixtures with truncated armored keys; user keys deleted/blanked in the gpgkeys table after partial migrations; passing an entity loaded from an event payload where fields were not hydrated; hand-modified database rows; upgrades between passbolt versions changing key validation rules.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at src/Service/OpenPGP/OpenPGPCommonUserOperationsTrait.php:43

trait OpenPGPCommonUserOperationsTrait
{
    /**
     * Get the OpenPGP Backend ready to encryption with user key
     *
     * @param \App\Utility\OpenPGP\OpenPGPBackend $gpg for example OpenPGPBackendFactory::get()
     * @param \App\Model\Entity\Gpgkey $userKey entity
     * @return \App\Utility\OpenPGP\OpenPGPBackend backend configured to use user key to encrypt
     * @throws \Cake\Http\Exception\InternalErrorException if the user key cannot be loaded
     */
    protected function setEncryptKeyWithUserKey(OpenPGPBackend $gpg, Gpgkey $userKey): OpenPGPBackend
    {
        // Set encryption key as the one from the user
        try {
            $this->assertUserKey($userKey);
        } catch (Exception $exception) {
            $msg = __('Could not validate user data.');
            throw new InternalErrorException($msg, 500, $exception);
        }
        try {
            $gpg->setEncryptKeyFromFingerprint($userKey->fingerprint);
        } catch (Exception $exception) {
            // Try to import the key in keyring again
            try {
                $gpg->importKeyIntoKeyring($userKey->armored_key);
                $gpg->setEncryptKeyFromFingerprint($userKey->fingerprint);
            } catch (Exception $exception) {
                if (Configure::read('debug')) {
                    Log::error(json_encode($userKey));
                }
                $msg = __('Could not import the user OpenPGP key.');
                throw new InternalErrorException($msg, 500, $exception);
            }
        }

        return $gpg;

View on GitHub (pinned to 31c1bbc10f)