passbolt/passbolt_api · error · App\Error\Exception\CustomValidationException

Could not validate password data.

Error message

Could not validate password data.

What it means

Thrown by AccountRecoveryPrivateKeyPasswordsValidationService::buildPasswordEntitiesFromDataOrFail after it builds password entities and rule-checks them: any entity whose errors() are non-empty (business rules or message composition failures) is collected, and if any errors exist a CustomValidationException is raised with the per-index entity errors under 'account_recovery_private_key_passwords'.

Solutions

  1. Inspect the per-index errors returned in the exception details under account_recovery_private_key_passwords and fix the flagged field(s)
  2. Verify each 'data' value is a valid OpenPGP message encrypted for the recipient user's account-recovery key
  3. Confirm every recipient user_id exists, is active, and has completed account-recovery setup (has a private key to receive the share)
  4. Retry entries one at a time to isolate the failing row before fixing the batch

Example fix

// before
{"user_id": "unknown-uuid", "data": "plaintext-secret"}
// after
{"user_id": "<existing-active-user-uuid>", "data": "<openpgp-message-encrypted-for-user-recovery-key>"}
Defensive patterns

Strategy: validation

Validate before calling

// Validate each entry before building entities:
foreach ($passwordsData as $i => $entry) {
    if (empty($entry['user_id']) || !Uuid::isValid($entry['user_id'])) {
        throw new \InvalidArgumentException("Entry {$i}: invalid user_id");
    }
    if (empty($entry['data']) || !str_starts_with($entry['data'], '-----BEGIN PGP MESSAGE-----')) {
        throw new \InvalidArgumentException("Entry {$i}: data must be an armored OpenPGP message");
    }
}

Type guard

$isValidEntry = fn(array $e): bool =>
    isset($e['user_id'], $e['data'], $e['private_key_id'])
    && Uuid::isValid($e['user_id']) && Uuid::isValid($e['private_key_id'])
    && is_string($e['data']) && $e['data'] !== '';

Try / catch

try {
    $entities = $validationService->buildPasswordEntitiesFromDataOrFail($uac, $data);
} catch (\App\Error\Exception\CustomValidationException $e) {
    $perIndexErrors = $e->getErrors()['account_recovery_private_key_passwords'];
    foreach ($perIndexErrors as $i => $errs) {
        // log/fix the specific fields failing on row $i
    }
}

Prevention

When it happens

Trigger: Creating/updating account recovery private key passwords where an entry fails entity-level rules — invalid user_id (recipient not found/not active), missing or malformed encrypted 'data', invalid private_key_id, or failed message composition (e.g. recipient has no configured account-recovery key).

Common situations: Passing plaintext instead of the expected encrypted message format; referencing a user who never completed account-recovery setup so their key/stamp is missing; schema/rounding issues in the base64 OpenPGP message payload; bulk payload where one bad row aborts the whole batch.

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/c945574ab8ca0c7c. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltEe/AccountRecovery/src/Service/AccountRecoveryPrivateKeyPasswords/AccountRecoveryPrivateKeyPasswordsValidationService.php:102

                $errors[$i]['recipient_fingerprint']['wrongRecipient'] = $msg;
                continue;
            }

            // Check subkey id in message packet
            if (!MessageRecipientValidationService::isMessageForRecipient($msgInfo, $keyInfo)) {
                $errors[$i]['data']['wrongRecipient'] = $msg;
                continue;
            }

            // Check business rules
            if (!$this->AccountRecoveryPrivateKeyPasswords->checkRules($entity)) {
                $errors[$i] = $entity->getErrors();
            }
        }

        // Throw an error on business rules or message composition
        if (count($errors)) {
            throw new CustomValidationException(__('Could not validate password data.'), [
                'account_recovery_private_key_passwords' => $errors,
            ]);
        }

        return $passwordEntities;
    }
}

View on GitHub (pinned to 31c1bbc10f)