passbolt/passbolt_api · error · Cake\Http\Exception\BadRequestException

Recovery response cannot be created when organization…

Error message

Recovery response cannot be created when organization policy is disabled.

What it means

Thrown as a BadRequestException when a client attempts to create an account recovery response while the organization-level account recovery policy is set to 'disabled'. The service explicitly blocks all recovery response creation because the feature is turned off organization-wide.

Solutions

  1. Enable the organization account recovery policy before creating responses (set the policy status via AccountRecoveryOrganizationSettings controller/service).
  2. Remove the client call that posts the recovery response when the feature is disabled.
  3. In API clients, check the policy state (GET /account-recovery/organization-policy) before posting a response.

Example fix

// before (policy disabled, request fails)
$post->post('/account-recovery/responses', $payload);
// after
$policy = $api->get('/account-recovery/organization-policy.json');
if ($policy->data->status !== 'disabled') {
    $post->post('/account-recovery/responses', $payload);
}
Defensive patterns

Strategy: validation

Validate before calling

$policy = (new AccountRecoveryOrganizationPolicyGetService())->get();
if ($policy->isDisabled()) { throw new RuntimeException('Account recovery is disabled for this organization.'); }

Type guard

function accountRecoveryEnabled(?\Passbolt\AccountRecovery\Model\Entity\AccountRecoveryOrganizationPolicy $p): bool {
    return $p !== null && !$p->isDisabled();
}

Try / catch

try {
    $service->create($uac, $data);
} catch (\Cake\Http\Exception\BadRequestException $e) {
    if ($e->getMessage() === 'Recovery response cannot be created when organization policy is disabled.') {
        // surface feature-disabled to user
    }
}

Prevention

When it happens

Trigger: POST to the account recovery responses endpoint while AccountRecoveryOrganizationPolicy is disabled; calling create() without first enabling the organization policy.

Common situations: Organization disabled account recovery after previously advertising it; clients with stale/recovery-enabled setups still posting responses; test environments where policy was never enabled.

Related errors


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

Appendix: source

Thrown at plugins/PassboltEe/AccountRecovery/src/Service/AccountRecoveryResponses/AccountRecoveryResponsesCreateService.php:128

            ? static::RESPONSE_APPROVED_EVENT_NAME
            : static::RESPONSE_REJECTED_EVENT_NAME;
        $event = new Event($eventName, $responseEntity);
        $this->AccountRecoveryResponses->getEventManager()->dispatch($event);

        return $responseEntity;
    }

    /**
     * @throws \Cake\Http\Exception\BadRequestException if organization policy is disabled
     * @return void
     */
    public function assertPolicyIsEnabled(): void
    {
        $service = new AccountRecoveryOrganizationPolicyGetService();
        $this->policy = $service->get();
        if ($this->policy->isDisabled()) {
            $msg = __('Recovery response cannot be created when organization policy is disabled.');
            throw new BadRequestException($msg);
        }
    }

    /**
     * @throws \App\Error\Exception\CustomValidationException if the request id is not set, not valid, not found, is not pending
     * @return \Passbolt\AccountRecovery\Model\Entity\AccountRecoveryRequest
     */
    public function assertAndGetAssociatedRequest(): AccountRecoveryRequest
    {
        $requestId = $this->getData('account_recovery_request_id');
        $msg = __('Could not validate response data.');

        if (!isset($requestId) || empty($requestId) || !is_string($requestId)) {
            throw new CustomValidationException($msg, [
                'account_recovery_request_id' => [
                    '_required' => 'The account recovery request id is required.',
                ],
            ]);

View on GitHub (pinned to 31c1bbc10f)