passbolt/passbolt_api · error · Cake\Http\Exception\BadRequestException
The request is already completed.
Error message
The request is already completed.
What it means
BadRequestException from getNotCompletedOrFail when the matched account recovery request is already in a completed status (approved, rejected or completed). Completed requests cannot be fetched/processed again through this path.
Solutions
- Start a new account recovery request (new token) instead of reusing the completed one
- Check the request's `status` before calling; only 'pending' requests are fetchable via this method
- If recovery was rejected/completed, follow the returned outcome (e.g. complete setup or normal login)
Example fix
// before getNotCompletedOrFail($requestId, ...) // status = 'approved' -> 400 // after create a fresh request via AccountRecoveryRequestCreateService, then get the new request id
Defensive patterns
Strategy: validation
Validate before calling
$request = $this->AccountRecoveryRequests->get($requestId); if ($request->isCompleted()) { // skip; start a new recovery flow instead } Type guard
null
Try / catch
try { $request = $service->get($requestId, $userId, $token); } catch (BadRequestException $e) { if (str_contains($e->getMessage(), 'already completed')) { startNewRecoveryFlow(); } } Prevention
- Track request status client-side; disable retries once completed
- Treat this as terminal: create a new request rather than reusing it
- Avoid double submission / auto-refresh of the recovery completion step
When it happens
Trigger: GET /account-recovery/requests/{id} for a request whose status has already transitioned out of 'pending' — e.g. an admin already responded, or a previous recovery attempt finalized it.
Common situations: Client double-submits or refreshes after the request was completed; admin approved/rejected while the user's client retries; user retries recovery after already completing it once.
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
- Recovery request cannot be created when user is not…
- User account recovery settings cannot be edited.
- Account recovery case must be a string.
- Account recovery is disabled.
- Account recovery reason not supported.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/dc461bf1b3916dfd.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/AccountRecovery/src/Service/AccountRecoveryRequests/AccountRecoveryRequestGetService.php:174
if (!Validation::uuid($requestId)) {
throw new BadRequestException(__('The request id is invalid.'));
}
try {
$where = [
'id' => $requestId,
'user_id' => $userEntity->id,
'authentication_token_id' => $tokenEntity->id,
];
/** @var \Passbolt\AccountRecovery\Model\Entity\AccountRecoveryRequest $requestEntity */
$requestEntity = $this->AccountRecoveryRequests->find()->where($where)->firstOrFail();
} catch (RecordNotFoundException $exception) {
$this->onRequestDoesNotExist($requestId, $userId, $clientIp ?? '0.0.0.0');
throw new NotFoundException(__('The account recovery request could not be found.'));
}
// Assert request is not already completed
if ($requestEntity->isCompleted()) {
throw new BadRequestException(__('The request is already completed.'));
}
// Assert token is not expired. If so, deactivate the token, reject the request and throw an exception
if ($tokenEntity->isExpired()) {
$requestEntity->set('status', AccountRecoveryRequest::ACCOUNT_RECOVERY_REQUEST_REJECTED);
$requestEntity->setAccess('status', true);
$this->AccountRecoveryRequests->saveOrFail($requestEntity);
$tokenService->getActiveNotExpiredOrFail($token, $userId, AuthenticationToken::TYPE_RECOVER);
}
return $requestEntity;
}
/**
* @param \Passbolt\AccountRecovery\Model\Entity\AccountRecoveryRequest $requestEntity entity
* @return array
*/
public function decorateResults(AccountRecoveryRequest $requestEntity): array
{View on GitHub (pinned to 31c1bbc10f)