BookStackApp/BookStack · error · UserRegistrationException
errors.error_user_exists_different_creds
Error message
errors.error_user_exists_different_creds
What it means
During registerUser(), after domain checks, BookStack checks whether a user with the given email already exists ($this->userRepo->getByEmail($userEmail)). If so, it throws UserRegistrationException with 'errors.error_user_exists_different_creds' (parameterized with the email), because auto-creating a duplicate account is unsafe — the email belongs to an account using different credentials.
Source
Thrown at app/Access/RegistrationService.php:87
}
/**
* The registrations flow for all users.
*
* @throws UserRegistrationException
*/
public function registerUser(array $userData, ?SocialAccount $socialAccount = null, bool $emailConfirmed = false): User
{
$userEmail = $userData['email'];
$authSystem = $socialAccount ? $socialAccount->driver : auth()->getDefaultDriver();
// Email restriction
$this->ensureEmailDomainAllowed($userEmail);
// Ensure the user does not already exist
$alreadyUser = !is_null($this->userRepo->getByEmail($userEmail));
if ($alreadyUser) {
throw new UserRegistrationException(trans('errors.error_user_exists_different_creds', ['email' => $userEmail]), '/login');
}
/** @var ?bool $shouldRegister */
$shouldRegister = Theme::dispatch(ThemeEvents::AUTH_PRE_REGISTER, $authSystem, $userData);
if ($shouldRegister === false) {
throw new UserRegistrationException(trans('errors.auth_pre_register_theme_prevention'), '/login');
}
// Create the user
$newUser = $this->userRepo->createWithoutActivity($userData, $emailConfirmed);
$newUser->attachDefaultRole();
// Assign a social account if given
if ($socialAccount) {
$newUser->socialAccounts()->save($socialAccount);
}
Activity::add(ActivityType::AUTH_REGISTER, $socialAccount ?? $newUser);View on GitHub (pinned to 18f8469a1c)
Solutions
- Match the external identity to the existing user: set the user's external_auth_id / system name to the incoming externalId (admin edit or SQL), or log in once via the original method and link accounts
- If the existing account is a duplicate/unused, rename or delete it so the external login can register cleanly
- Ensure your IdP issues stable external ids so findOrRegister matches by id instead of email
- Check the email domain/claims config isn't mapping two distinct external users to one email
Example fix
-- before: existing local user with no external id -- after: link external id so login matches the existing account UPDATE users SET external_auth_id='<idp-sub-value>' WHERE email='user@example.com';
Defensive patterns
Strategy: try-catch
Validate before calling
// Detect the conflict before registration:
$existing = $userRepo->getByEmail($incomingEmail);
if ($existing !== null && $existing->external_auth_id !== $incomingExternalId) {
// email taken by different credentials — link accounts or resolve manually
} Try / catch
try {
auth()->attemptOidcLogin();
} catch (BookStack\Access\Oidc\OidcException $e) {
if (str_contains($e->getMessage(), 'error_user_exists_different_creds') || str_contains($e->getMessage(), 'already')) {
return redirect('/login')->withErrors(['email' => 'An account with this email exists; sign in with the original method or ask an admin to link accounts']);
}
throw $e;
} Prevention
- Keep external_auth_id stable and populated so matching works by id
- Migrate accounts explicitly (set external_auth_id) when switching auth systems
- Prevent duplicate emails across IdPs with provisioning checks
- Document an admin runbook for resolving email conflicts
When it happens
Trigger: registerUser() is invoked via findOrRegister() during OIDC (or similar external) login; the external identity's email matches an existing BookStack user that was registered under a different auth system (e.g. local password, or different IdP), so no account match by external id exists.
Common situations: User previously signed up locally with the same email then tries to log in via OIDC/SAML/LDAP; email changed at the IdP to collide with an existing account; migrating between auth systems without unifying accounts; duplicate emails across IdPs.
Related errors
- {$exception->getMessage()}
- errors.email_already_confirmed
- Token audience value has ' . count($aud) . ' values, Expecte
- Token authorized party exists but does not match the expecte
- Missing token expiration time value
AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02).
Data as JSON: /api/errors/097711bb2489b153.
Report an issue: GitHub.