passbolt/passbolt_api · error · Cake\Http\Exception\BadRequestException
User account recovery settings cannot be edited.
Error message
User account recovery settings cannot be edited.
What it means
Thrown in AccountRecoveryUserSettingsSetService::set() when the user's existing account recovery setting is already 'approved'. Policy allows a user to enroll only once: re-enrollment, de-enrollment or any edit of an approved setting is rejected with a BadRequestException.
Solutions
- Fetch current settings first and skip the save if already approved
- Return the existing setting idempotently for repeated approved submissions
- For a genuine policy change (de-enroll/re-enroll), an administrator must reset the setting per organization policy
- Fix client-side double submission (disable submit, dedupe retries)
Example fix
// before
$service->set($data); // throws if already approved
// after
$current = (new AccountRecoveryUserSettingsGetService())->get($userId);
if (!$current || !$current->isApproved()) {
$service->set($data);
} Defensive patterns
Strategy: validation
Validate before calling
const current = await getUserSetting(userId); if (current && current.status === 'approved') return current; // skip save
Type guard
const canEdit = (s) => s == null || s.status !== 'approved';
Try / catch
try { await setSettings(data); } catch (e) { if (e.status === 400 && /cannot be edited/.test(e.message)) { /* reload and show current state */ } } Prevention
- Fetch current settings before every save
- Make saves idempotent by short-circuiting when already approved
- Debounce/dedupe form submissions
When it happens
Trigger: PATCH/POST to the account recovery user settings endpoint with status=approved when the current setting is already approved; replaying an enrollment request; a client re-submitting settings after a successful enroll.
Common situations: Double-submission of the enrollment form (network retry, double click); clients not fetching current settings before saving; tests reusing a fixture user that is already enrolled.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- The request is already completed.
- Account recovery case must be a string.
- Account recovery is disabled.
- Account recovery reason not supported.
- An authentication token should be provided.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/a88f3913687a71a1.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/AccountRecovery/src/Service/AccountRecoveryUserSettings/AccountRecoveryUserSettingsSetService.php:96
->fetchTable('Passbolt/AccountRecovery.AccountRecoveryUserSettings');
$this->AccountRecoveryPrivateKeys = $this
->fetchTable('Passbolt/AccountRecovery.AccountRecoveryPrivateKeys');
$this->AccountRecoveryPrivateKeyPasswords = $this
->fetchTable('Passbolt/AccountRecovery.AccountRecoveryPrivateKeyPasswords');
$this->uac = $uac;
}
/**
* @param array $data Payload
* @return \Passbolt\AccountRecovery\Model\Entity\AccountRecoveryUserSetting
*/
public function set(array $data): AccountRecoveryUserSetting
{
// Ensure user can only enroll once
// It's not possible for a user to enroll and de-enroll or enroll and re-enroll
$currentSettings = (new AccountRecoveryUserSettingsGetService())->get($this->uac->getId());
if (isset($currentSettings) && $currentSettings->isApproved()) {
throw new BadRequestException(__('User account recovery settings cannot be edited.'));
}
$setting = $this->patchEntity($data);
$this->AccountRecoveryUserSettings->saveOrFail($setting);
return $setting;
}
/**
* @param array $data Payload
* @return \Passbolt\AccountRecovery\Model\Entity\AccountRecoveryUserSetting
*/
public function patchEntity(array $data): AccountRecoveryUserSetting
{
$this->data = $data;
$this->organizationPolicy = (new AccountRecoveryOrganizationPolicyGetService())->getOrFail();
$status = $data['status'] ?? '';
$setting = $this->validateAccountRecoveryUserSetting($status);View on GitHub (pinned to 31c1bbc10f)