passbolt/passbolt_api · error · BadRequestException

The user id is invalid.

Error message

The user id is invalid.

What it means

BadRequestException thrown by AccountRecoveryContinueController::get() when the userId route parameter is missing or not a valid UUID. The controller validates all identifier parameters before delegating to services.

Solutions

  1. Regenerate the recovery link ensuring the full user UUID is included
  2. Validate the user id is a UUID before making the request (Cake\Validation::uuid())
  3. Check the email template/link builder produces the correct route
  4. Log the incoming URL to confirm which parameter is malformed

Example fix

// before
$url = "/account-recovery/continue/$userId/$tokenId.json"; // $userId = 'admin@example.com'
// after
$user = $usersTable->findByUsername($email)->firstOrFail();
$url = "/account-recovery/continue/{$user->id}/$tokenId.json";
Defensive patterns

Strategy: validation

Validate before calling

import { uuidValidation } from 'passbolt/styleguide/lib/assertions/uuid.validation';
if (!uuidValidation(userId)) throw new Error('user id must be a UUID');

Type guard

function isValidUuid(value: unknown): value is string {
  return typeof value === 'string'
    && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
}

Try / catch

try {
  await accountRecoveryContinueService.get(userId, tokenId);
} catch (ApiError e) {
  if (e.message.includes('user id is invalid')) {
    redirectToRecoveryStart(); // regenerate a valid link
  }
}

Prevention

When it happens

Trigger: GET request to the account recovery continue endpoint with a missing, empty, or malformed (non-UUID) user id in the URL, e.g. /account-recovery/continue/not-a-uuid/<token-id>.json

Common situations: Broken or hand-edited recovery link from an email template; truncated URL; client sending a username/email instead of the user UUID; old links generated before ID format changes.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at plugins/PassboltEe/AccountRecovery/src/Controller/AccountRecoveryContinue/AccountRecoveryContinueController.php:63

        parent::beforeFilter($event);
    }

    /**
     * Render a page to continue the account recovery process
     *
     * @param string|null $userId User ID
     * @param string|null $tokenId Token ID
     * @param \Passbolt\Ee\Service\AccountRecoveryContinue\AccountRecoveryContinueAggregatorService $accountRecoveryContinueService Service instance.
     * @return void
     * @throws \Cake\Http\Exception\BadRequestException if the data provided is not valid
     */
    public function get(
        ?string $userId,
        ?string $tokenId,
        AccountRecoveryContinueAggregatorService $accountRecoveryContinueService
    ): void {
        if (!isset($userId) || !Validation::uuid($userId)) {
            throw new BadRequestException(__('The user id is invalid.'));
        }
        if (!isset($tokenId) || !Validation::uuid($tokenId)) {
            throw new BadRequestException(__('The authentication token id is invalid.'));
        }

        if ($this->getRequest()->is('json')) {
            // Do not allow logged in user to recover
            if ($this->User->role() !== Role::GUEST) {
                throw new ForbiddenException(__('Only guests are allowed to proceed with account recovery.'));
            }

            (new AccountRecoveryRequestGetService())->getOrFail($userId, $tokenId);

            $data = $accountRecoveryContinueService->get();

            $this->success(__('The operation was successful.'), $data);
        } else {
            $this->renderHtml();

View on GitHub (pinned to 31c1bbc10f)