passbolt/passbolt_api · error · BadRequestException

The account recovery request status is not approved.

Error message

The account recovery request status is not approved.

What it means

Thrown during account recovery setup/recover completion when the pending account recovery request associated with the authentication token exists but its status is not 'approved'. Passbolt only allows completing a recovery workflow once an administrator has approved the request, so any other status (pending, rejected, etc.) is rejected as a bad request.

Solutions

  1. Have an administrator review and approve the account recovery request (Admin Workspace > Account Recovery) so its status becomes 'approved', then retry the complete call
  2. Restart the recovery flow from the beginning to generate a fresh request, and ensure it is approved before completing
  3. Verify the organization account recovery policy is enabled and the request corresponds to the current user/token pair (stale tokens reference old requests)
  4. Check the account_recovery_requests table status for the user to confirm the workflow state before debugging client code

Example fix

// before: completing with a pending request
await passbolt.setupRecoverComplete(userId, tokenId);
// after: ensure the request is approved first (admin action), then retry;
// client can pre-check via GET /account-recovery/requests for status === 'approved'
Defensive patterns

Strategy: try-catch

Validate before calling

const req = await fetch(`/account-recovery/requests?user_id=${userId}`);
const status = (await req.json())?.status;
if (status !== 'approved') throw new Error('Request not approved yet');

Type guard

const isApproved = (r) => r && typeof r.status === 'string' && r.status === 'approved';

Try / catch

try {
  await passbolt.accountRecovery.complete(userId, tokenId);
} catch (e) {
  if (e.code === 400 && /not approved/.test(e.message)) {
    notifyAdminForApproval();
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling the setup/recover complete endpoint (POST /setup/recover/complete/<userId>/<tokenId> or setup complete) where the AccountRecoveryRequests row linked to the user still has status other than 'approved' — e.g. the admin never approved the request, or the request was created/recreated after approval, or a stale token references a superseded request.

Common situations: Admin approval workflow not finished before the user completes setup; user re-initiates account recovery (creating a new pending request) but the browser still holds an old approved token flow; environment restored from backup with reset request statuses; testing the recover flow without running the organization recovery settings approval step.

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


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

Appendix: source

Thrown at plugins/PassboltEe/AccountRecovery/src/Service/Setup/AccountRecoveryRecoverCompleteService.php:86

                    'AuthenticationTokens.token' => $this->request->getData('authenticationtoken.token'),
                ]);
            })
            ->where([
                'AccountRecoveryRequests.id' => $requestId,
                'AccountRecoveryRequests.user_id' => $token->user_id,
            ])
            ->contain('AccountRecoveryResponses', function (Query $query) {
                return $query
                    ->select([
                        'AccountRecoveryResponses.id',
                        'AccountRecoveryResponses.account_recovery_request_id',
                        'AccountRecoveryResponses.data',
                    ]);
            })
            ->firstOrFail();

        if (!$request->isApproved()) {
            throw new BadRequestException(__('The account recovery request status is not approved.'));
        }

        $this->AuthenticationTokens->hasOne('Passbolt/AccountRecovery.AccountRecoveryRequests');
        $request->setAccess([
            'status',
            'modified_by',
            'account_recovery_responses',
        ], true);
        $request->status = AccountRecoveryRequest::ACCOUNT_RECOVERY_REQUEST_COMPLETED;
        $request->modified_by = $token->user_id;

        foreach ($request->account_recovery_responses as $response) {
            $response->setAccess(['data', 'modified_by'], true);
            $response->data = null;
            $response->modified_by = $token->user_id;
        }
        $request->setDirty('account_recovery_responses');
        $token->set('account_recovery_request', $request);

View on GitHub (pinned to 31c1bbc10f)