passbolt/passbolt_api · error · Cake\Http\Exception\NotFoundException
The user does not exist or has been deleted.
Error message
The user does not exist or has been deleted.
What it means
findUser loads the user via Users->findView($userId, Role::GUEST) with its Gpgkey, logging any query exception and converting it to NotFoundException 'The user does not exist or has been deleted.' This first raise covers the case where the query itself throws (invalid UUID, DB error) or is otherwise exceptional. It guards the JWT flow against tokens referencing missing users.
Solutions
- Re-authenticate to obtain a fresh token: the token's user id is invalid, so it cannot be reused.
- Verify the user actually exists and is not deleted (users back-end listing / database).
- Check logs for the logged exception message to distinguish malformed id vs DB failure.
- Purge invalid refresh tokens for deleted users so clients fall back to full login.
- If DB errors recur, check database health and migration status.
Defensive patterns
Strategy: try-catch
Validate before calling
const isUuid = (v) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v);
if (!isUuid(subClaim)) forceFullLogin(); Try / catch
try { await authenticatedCall(); } catch (e) { if (e.message.includes('does not exist or has been deleted')) { clearTokens(); redirectToLogin(); } else throw e; } Prevention
- Clear stored tokens when a user is deleted from the instance.
- On this 404, always fall back to a fresh login rather than retrying the token.
- Validate the token 'sub' is a UUID before sending requests.
- Keep environments' databases and issued tokens consistent.
When it happens
Trigger: Presenting a JWT whose 'sub' claim contains a user id that is malformed (not a UUID), fails the view query, or triggers a DB exception during lookup during GPG-JWT authentication.
Common situations: Stale tokens surviving a user hard-delete; corrupted or hand-crafted JWT payloads; database connectivity errors during authentication; tokens issued before a server migration changed user data.
Understand the failure class
Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.
Related errors
- No active refresh token matching the request could be found.
- No active refresh token matching the request could be found.
- Attempt to access an expired verify token.
- Could not import the user OpenPGP key.
- Expired refresh token provided.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/6302e08f63614682.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/JwtAuthentication/src/Authenticator/GpgJwtAuthenticator.php:284
/**
* @param string $userId uuid
* @throws \Cake\Http\Exception\NotFoundException if the user cannot be found, is deleted, is not active
* @return \App\Model\Entity\User
*/
private function findUser(string $userId): User
{
try {
/** @var \App\Model\Table\UsersTable $Users */
$Users = TableRegistry::getTableLocator()->get('Users');
/** @var \App\Model\Entity\User|null $userData */
$userData = $Users->findView($userId, Role::GUEST)
->contain('Gpgkeys')
->first();
} catch (Exception $exception) {
Log::error($exception->getMessage());
throw new NotFoundException(__('The user does not exist or has been deleted.'));
}
if (!isset($userData)) {
throw new NotFoundException(__('The user does not exist or has been deleted.'));
}
if ($userData->isDisabled()) {
throw new NotFoundException(__('The user does not exist or has been deleted.'));
}
return $userData;
}
/**
* @throws \InvalidArgumentException if the challenge is missing
* @throws \Cake\Http\Exception\BadRequestException if the challenge is invalid
* @return string
*/View on GitHub (pinned to 31c1bbc10f)