immich-app/immich · error · BadRequestException
OAuth authentication failed
Error message
OAuth authentication failed
What it means
Thrown by callback() during account linking: a user exists with the profile's email but that user is already linked to a different oauthId, so the server refuses to link and fails authentication, preventing account takeover via email collision.
Solutions
- Unlink the existing OAuth account (admin user settings) or clear its oauthId, then log in again to relink
- Delete or merge the stale duplicate account
- Ensure the provider returns a stable sub claim
- Verify the intended provider/issuer is configured
Example fix
// before // alice@x.com has oauthId 'old-sub', logs in via 'new-sub' -> 400 // after // admin: unlink OAuth from alice's account, then retry login to relink with 'new-sub'
Defensive patterns
Strategy: try-catch
Try / catch
try { await api.oauthCallback(dto, headers) } catch (e) { if (e.status === 400 && /OAuth authentication failed/.test(e.message)) { /* check for account already linked to another oauthId */ } throw e; } Prevention
- Use one OAuth provider consistently per server
- Keep provider subject IDs stable across migrations
- Avoid duplicate emails across users
When it happens
Trigger: OAuth profile's email matches an Immich user whose oauthId differs from profile.sub; user previously linked to another provider/subject.
Common situations: Switching OAuth providers (same emails, new subject IDs); duplicate accounts sharing an email; provider re-issuing sub values.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- OAuth link account failed: sub is already linked to another…
- This OAuth account has already been linked to another user.
- OAuth profile does not have an email address
- Email is not available
- Email is not available
AI-assisted analysis of immich-app/immich@f48d4b3321 (2026-09-15).
Data as JSON: /api/errors/d8f013e80cd1576c.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/auth.service.ts:322
const url = this.resolveRedirectUri(oauth, dto.url);
const {
profile,
sid: oauthSid,
idToken: oauthBearerToken,
} = await this.oauthRepository.getProfileAndOAuthSid(oauth, url, expectedState, codeVerifier);
const normalizedEmail = profile.email ? profile.email.trim().toLowerCase() : undefined;
const { autoRegister, defaultStorageQuota, storageLabelClaim, storageQuotaClaim, roleClaim } = oauth;
this.logger.debug(`Logging in with OAuth: ${JSON.stringify(profile)}`);
let user: UserAdmin | undefined = await this.userRepository.getByOAuthId(profile.sub);
// link by email
if (!user && normalizedEmail) {
const emailUser = await this.userRepository.getByEmail(normalizedEmail);
if (emailUser) {
if (emailUser.oauthId) {
this.logger.debug('OAuth login conflict: email already linked to different account');
throw new BadRequestException('OAuth authentication failed');
}
user = await this.userRepository.update(emailUser.id, { oauthId: profile.sub });
}
}
const role = this.getRoleClaim(profile, roleClaim);
const isAdmin = role === 'admin';
if (user && role && isAdmin !== user.isAdmin) {
user = await this.userRepository.update(user.id, { isAdmin });
}
// register new user
if (!user) {
if (!autoRegister) {
this.logger.warn(
`Unable to register ${profile.sub}/${normalizedEmail || '(no email)'}. User does not exist and auto registering is disabled. To enable set OAuth Auto Register to true in admin settings.`,
);View on GitHub (pinned to f48d4b3321)