immich-app/immich · warning · BadRequestException
Invalid user
Error message
Invalid user
What it means
PartnerService.create verifies that the sharedWithId references an existing user via userRepository.get. If the user does not exist, it logs 'Partner creation failed: user not found' and throws BadRequestException('Invalid user'). This prevents dangling partner rows pointing at nonexistent users.
Solutions
- Confirm the sharedWithId is an existing, active user (GET /api/users) before creating the partner.
- Remove the deleted user's stale references or pick a valid user from the user list.
- If IDs came from a backup/restore, re-sync the database so user rows exist.
- Handle the 400 response in the UI by refreshing the selectable user list.
Example fix
// before
await api.partnersApi.createPartner({ sharedWithId: staleUserId });
// after
const users = await api.usersApi.searchUsers();
const target = users.find((u) => u.id === sharedWithId);
if (!target) throw new Error(`User ${sharedWithId} does not exist`);
await api.partnersApi.createPartner({ sharedWithId: target.id }); Defensive patterns
Strategy: validation
Validate before calling
const users = await api.usersApi.searchUsers();
const target = users.find((u) => u.id === sharedWithId);
if (!target) throw new Error(`User ${sharedWithId} not found — refresh user list`); Try / catch
try {
await api.partnersApi.createPartner({ sharedWithId });
} catch (e) {
if (e.status === 400 && String(e.message).includes('Invalid user')) {
await refreshUserList(); // user deleted or wrong ID
} else throw e;
} Prevention
- Always populate share targets from a fresh GET /api/users call, never cached UUIDs.
- Handle user deletion events by invalidating cached share candidates.
- Validate the sharedWithId is a non-empty UUID client-side.
When it happens
Trigger: POST /api/partners with a sharedWithId that is not a valid user ID (deleted user, typo, or UUID from another instance).
Common situations: Sharing with a user whose account was removed, copy-pasting a stale UUID, sharing with yourself (self not resolvable as expected), or cross-instance IDs after a restore/migration.
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
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/9ee0b9628787a0f6.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/partner.service.ts:22
import { PartnerCreateDto, PartnerResponseDto, PartnerSearchDto, PartnerUpdateDto } from 'src/dtos/partner.dto.js';
import { mapUser } from 'src/dtos/user.dto.js';
import { Permission } from 'src/enum.js';
import { PartnerDirection, PartnerIds } from 'src/repositories/partner.repository.js';
import { BaseService } from 'src/services/base.service.js';
@Injectable()
export class PartnerService extends BaseService {
async create(auth: AuthDto, { sharedWithId }: PartnerCreateDto): Promise<PartnerResponseDto> {
const partnerId: PartnerIds = { sharedById: auth.user.id, sharedWithId };
const exists = await this.partnerRepository.get(partnerId);
if (exists) {
throw new BadRequestException(`Partner already exists`);
}
const user = await this.userRepository.get(sharedWithId, {});
if (!user) {
this.logger.debug('Partner creation failed: user not found');
throw new BadRequestException('Invalid user');
}
const partner = await this.partnerRepository.create(partnerId);
return this.mapPartner(partner, PartnerDirection.SharedBy);
}
async remove(auth: AuthDto, sharedWithId: string): Promise<void> {
const partnerId: PartnerIds = { sharedById: auth.user.id, sharedWithId };
const partner = await this.partnerRepository.get(partnerId);
if (!partner) {
throw new BadRequestException('Partner not found');
}
await this.partnerRepository.remove(partnerId);
}
async search(auth: AuthDto, { direction }: PartnerSearchDto): Promise<PartnerResponseDto[]> {
const partners = await this.partnerRepository.getAll(auth.user.id);View on GitHub (pinned to e55ac299a4)