passbolt/passbolt_api · error · CustomValidationException

Invalid request. You cannot opt-out.

Error message

Invalid request. You cannot opt-out.

What it means

assertRules() throws this CustomValidationException when the organization policy is mandatory and the resulting setting is not approved. Unlike the plain BadRequest variant, it carries structured errors (status.isMandatoryRule) for form rendering.

Solutions

  1. Re-fetch the organization policy before submitting and force status=approved when mandatory
  2. Update/refresh clients so they honor the current policy
  3. If opt-out is legitimately required, change the organization policy first

Example fix

// before
const status = 'rejected';
// after
const status = organizationPolicy.isMandatory ? 'approved' : 'rejected';
Defensive patterns

Strategy: validation

Validate before calling

if (orgPolicy.isMandatory && submittedStatus !== 'approved') throw new Error('policy is mandatory: status must be approved');

Type guard

const isCompliant = (policy, s) => !policy.isMandatory || s === 'approved';

Try / catch

try { await setSettings(data); } catch (e) { if (e.body?.account_recovery_user_setting?.status?.isMandatoryRule) { /* force approved */ } }

Prevention

When it happens

Trigger: Any submitted setting that ends up non-approved (rejected, or other non-approved status) while organization policy is mandatory; same family as error 542 but raised through the entity-rule path of patchEntity.

Common situations: Old client versions caching a non-mandatory policy and sending opt-out; users bypassing the UI with direct API calls; org policy changed to mandatory mid-flow.

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

Appendix: source

Thrown at plugins/PassboltEe/AccountRecovery/src/Service/AccountRecoveryUserSettings/AccountRecoveryUserSettingsSetService.php:166

            return $this->AccountRecoveryUserSettings->buildAndValidateEntity($this->uac, $status);
        } catch (ValidationException $exception) {
            throw new CustomValidationException($exception->getMessage(), [
                'account_recovery_user_setting' => $exception->getErrors(),
            ]);
        }
    }

    /**
     * Check that the user selected setting makes sense as per select org policy
     *
     * @param \Passbolt\AccountRecovery\Model\Entity\AccountRecoveryUserSetting $setting entity
     * @throws \Cake\Http\Exception\BadRequestException if user rejects and policy is mandatory
     * @return void
     */
    protected function assertRules(AccountRecoveryUserSetting $setting): void
    {
        if ($this->organizationPolicy->isMandatory() && !$setting->isApproved()) {
            throw new CustomValidationException(__('Invalid request. You cannot opt-out.'), [
                'account_recovery_user_setting' => [
                    'status' => [
                        'isMandatoryRule' => __('The status must be set to approved.'),
                    ],
                ],
            ]);
        }

        if (!$setting->isApproved() && ($this->isPrivateKeyProvided() || $this->arePasswordsProvided())) {
            throw new CustomValidationException(__('Invalid request. You cannot both opt-out and provide backup.'), [
                'account_recovery_user_setting' => [
                    'status' => [
                        'isMatchingData' => __('The status must be set to approved.'),
                    ],
                ],
            ]);
        }

View on GitHub (pinned to 31c1bbc10f)