immich-app/immich · error · BadRequestException
User not found
Error message
User not found
What it means
A BadRequestException (HTTP 400) thrown by UserService.getMe when the authenticated user's record cannot be loaded from the database. The auth guard already validated the session/JWT, so reaching this branch means the user row is gone between authentication and the getMe query. It is a data-integrity signal rather than a normal client error.
Source
Thrown at server/src/services/user.service.ts:46
export class UserService extends BaseService {
async search(auth: AuthDto): Promise<UserResponseDto[]> {
const config = await this.getConfig({ withCache: false });
let users;
if (auth.user.isAdmin || config.server.publicUsers) {
users = await this.userRepository.getList({ withDeleted: false });
} else {
const authUser = await this.userRepository.get(auth.user.id, {});
users = authUser ? [authUser] : [];
}
return users.map((user) => mapUser(user));
}
async getMe(auth: AuthDto): Promise<UserAdminResponseDto> {
const user = await this.userRepository.get(auth.user.id, {});
if (!user) {
throw new BadRequestException('User not found');
}
return mapUserAdmin(user);
}
getCalendarHeatmap(auth: AuthDto, dto: CalendarHeatmapDto): Promise<CalendarHeatmapResponseDto> {
return getCalendarHeatmap(auth.user.id, dto, { asset: this.assetRepository });
}
async updateMe({ user }: AuthDto, dto: UserUpdateMeDto): Promise<UserAdminResponseDto> {
if (dto.email) {
const duplicate = await this.userRepository.getByEmail(dto.email);
if (duplicate && duplicate.id !== user.id) {
this.logger.warn('Email already in use by another account');
throw new BadRequestException('Email is not available');
}
}
View on GitHub (pinned to 199723261c)
Solutions
- If this is an end-user client, treat it as a forced logout: clear the local session/token and redirect to login.
- If you are an admin automating user lifecycle, ensure no getMe calls are in flight when you delete a user; tear down sessions first.
- Check the user table for the id in the token to confirm whether the row was deleted or the id is malformed.
- In tests, re-authenticate or skip getMe after deleting the acting user.
Example fix
// before
const me = await api.getUserMe(); // 400 'User not found'
// after
try {
const me = await api.getUserMe();
} catch (e) {
if (e.status === 400 && e.message === 'User not found') {
await auth.clearSession();
router.push('/login');
return;
}
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// No client-side validation can guarantee the row exists; verify right before the call
const stillExists = await adminApi.getUser(authUserId);
if (!stillExists) { await clearSession(); return; } Type guard
const isUserMissingError = (e: unknown): boolean => typeof e === 'object' && e !== null && (e as any).status === 400 && (e as any).message === 'User not found';
Try / catch
try {
return await api.getUserMe();
} catch (e) {
if (isUserMissingError(e)) {
await auth.clearSession();
redirectToLogin();
return null;
}
throw e;
} Prevention
- Treat 'User not found' on getMe as a forced logout, not a retryable error.
- In tests, never call getMe after deleting the acting user.
- When deleting users administratively, revoke their sessions in the same operation to avoid stale-token calls.
When it happens
Trigger: GET /users/me issued after the user was soft- or hard-deleted (e.g., by an admin) but the client still holds a valid session token. Also reachable via test harnesses that authenticate then delete the user, or a race where a deletion job commits between auth and the repository.get call.
Common situations: Stale tokens in a browser after an admin deletes the account; integration tests that delete users mid-flow; database restores that drop user rows but keep sessions; multi-instance setups where deletion replication lags.
Related errors
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/5f4f1e179ae5e464.
Report an issue: GitHub.