passbolt/passbolt_api · error · ValidationException
Could not save the account recovery private key.
Error message
Could not save the account recovery private key.
What it means
Thrown when the account_recovery_private_key association attached to the user entity fails validation while saving during account recovery setup completion. The private key to be escrowed is malformed (e.g. not a valid OpenPGP key message) or violates entity rules, and the ValidationException exposes the errors plus the AccountRecoveryPrivateKeys table.
Solutions
- Check the errors object in the ValidationException response to identify the failing key field
- Verify the private key is armored, complete, and encrypted for the organization recovery key before sending
- Ensure the key is generated/encrypted with a supported OpenPGP implementation (GnuPG/OpenPGP.js as used by the extension)
- Regenerate the recovery data by restarting the setup flow with a current browser extension version
Example fix
// before
keyPayload = { data: rawPrivateKeyString }; // unencrypted/invalid
// after
keyPayload = { data: await encryptForOrgRecoveryKey(privateKeyArmored) }; Defensive patterns
Strategy: try-catch
Validate before calling
if (!/-----BEGIN PGP MESSAGE-----/.test(payload.account_recovery_private_key?.data ?? '')) {
throw new Error('Recovery private key must be an armored encrypted message');
} Type guard
const isValidRecoveryKey = (k) => k != null && typeof k.data === 'string' && k.data.startsWith('-----BEGIN PGP MESSAGE-----'); Try / catch
try {
await setupComplete(payload);
} catch (e) {
if (e.name === 'ValidationException' && e.errors?.account_recovery_private_key) {
restartKeyEncryptionStep();
} else { throw e; }
} Prevention
- Always encrypt the recovery private key with the organization recovery public key
- Use the OpenPGP library shipped with the extension rather than custom crypto
- Check the errors object in the 400 response for the exact field problem
- Restart the setup flow to regenerate recovery data after encryption failures
When it happens
Trigger: POST setup complete where the provided account recovery private key entity has validation errors — invalid armored key, missing required fields (data, user_id), or key not decryptable/associated correctly.
Common situations: Client encrypts the recovery key with the wrong organization key; corrupted or truncated armored key in the payload; custom integrations posting raw keys instead of the expected structure; key generated by an unsupported OpenPGP implementation.
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
- Could not save the account recovery setting.
- Could not validate key revocation.
- Could not validate policy data.
- Could not validate public key data.
- The OpenPGP key can not be used to encrypt.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/0ba5880605e3b412.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/AccountRecovery/src/Service/Setup/AccountRecoverySetupCompleteService.php:188
* @param \App\Model\Entity\User $user User entity
* @param array|null $saveOptions options
* @return \App\Model\Entity\User
*/
protected function saveUserEntity(User $user, ?array $saveOptions = []): User
{
$user = parent::saveUserEntity($user, $saveOptions);
if ($this->isAccountRecoveryUserSettingProvided()) {
if ($user->get('account_recovery_user_setting')->hasErrors()) {
throw new ValidationException(
'Could not save the account recovery setting.',
$user->get('account_recovery_user_setting'),
$this->AccountRecoveryUserSettings
);
}
if ($user->hasValue('account_recovery_private_key') && $user->get('account_recovery_private_key')->hasErrors()) { // phpcs:ignore
throw new ValidationException(
'Could not save the account recovery private key.',
$user->get('account_recovery_private_key'),
$this->AccountRecoveryPrivateKeys
);
}
}
return $user;
}
}
View on GitHub (pinned to 31c1bbc10f)