passbolt/passbolt_api · error · BadRequestException

Account recovery is disabled. Key backup is not supported.

Error message

Account recovery is disabled. Key backup is not supported.

What it means

Thrown in AccountRecoverySetupCompleteService::assertRequestSanity when the organization account recovery policy is 'disabled' yet the client still sends account recovery user setting data (e.g. a recovery preference) in the setup complete payload. With recovery disabled, key backup is unsupported, so any recovery-related payload is rejected as a bad request.

Solutions

  1. Remove the account recovery user setting data from the setup complete request payload and retry
  2. Reload the setup page so the client fetches the current organization recovery policy and hides backup options
  3. If backup should be available, re-enable the account recovery policy in the admin settings (with correct EE license)
  4. Clear browser cache / update the passbolt web extension to the version matching the server policy

Example fix

// before
payload = { ...baseData, account_recovery_user_setting: 'enabled' };
// after (policy disabled)
payload = { ...baseData }; // no recovery fields when policy is disabled
Defensive patterns

Strategy: validation

Validate before calling

const policy = await getOrgPolicy();
if (policy === 'disabled' && 'account_recovery_user_setting' in payload) {
  delete payload.account_recovery_user_setting;
}

Type guard

const recoveryDataProvided = (p) => p != null && ('account_recovery_user_setting' in p || 'account_recovery_private_key' in p);

Try / catch

try {
  await setupComplete(payload);
} catch (e) {
  if (e.code === 400 && /disabled/.test(e.message)) {
    payload = stripRecoveryFields(payload);
    await setupComplete(payload);
  } else { throw e; }
}

Prevention

When it happens

Trigger: POST /setup/setup/complete (or account recovery setup complete) where the org policy is disabled and the request body contains account_recovery_user_setting (or related recovery fields).

Common situations: Organization administrator disabled account recovery after the client UI was loaded (stale UI still offers 'save my key on server'); a cached/stale front-end bundle posting legacy recovery fields; misconfigured environment where the EE account recovery setting was toggled off mid-setup.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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

Appendix: source

Thrown at plugins/PassboltEe/AccountRecovery/src/Service/Setup/AccountRecoverySetupCompleteService.php:130

                return $this->saveUserEntity($user, $saveOptions);
            }
        );
    }

    /**
     * Assert that there is not too much or not enough data
     * Mandatory: both private key and password must be provided
     * Disabled: none of them must be provided
     *
     * @return void
     * @throws \Cake\Http\Exception\BadRequestException if data is missing or too much data is sent
     */
    protected function assertRequestSanity(): void
    {
        if ($this->policy->isDisabled()) {
            if ($this->isAccountRecoveryUserSettingProvided()) {
                throw new BadRequestException(__('Account recovery is disabled. Key backup is not supported.'));
            }
        } elseif ($this->policy->isMandatory()) {
            if (!$this->isPrivateKeyProvided() || !$this->arePasswordsProvided()) {
                throw new BadRequestException(
                    __('Account recovery is mandatory. Please provide the mandatory data.')
                );
            }
        }
    }

    /**
     * @return bool true if the account_recovery_user_setting data is set
     */
    protected function isAccountRecoveryUserSettingProvided(): bool
    {
        return is_array($this->request->getData('account_recovery_user_setting'));
    }

View on GitHub (pinned to 31c1bbc10f)