{"record":{"id":"9d61aca4f1842906","repo":"immich-app/immich","slug":"invalid-user","errorCode":null,"errorMessage":"Invalid user","messagePattern":"Invalid user","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/album.service.ts","lineNumber":108,"sourceCode":"\n  async getMapMarkers(auth: AuthDto, id: string): Promise<MapMarkerResponseDto[]> {\n    await this.requireAccess({ auth, permission: Permission.AlbumRead, ids: [id] });\n\n    if (auth.sharedLink && !auth.sharedLink.showExif) {\n      return [];\n    }\n\n    return this.mapRepository.getAlbumMapMarkers(id);\n  }\n\n  async create(auth: AuthDto, dto: CreateAlbumDto): Promise<AlbumResponseDto> {\n    const albumUsers = (dto.albumUsers || []).filter(({ userId }) => userId !== auth.user.id);\n\n    for (const { userId } of albumUsers) {\n      const exists = await this.userRepository.get(userId, {});\n      if (!exists) {\n        this.logger.debug('Album creation failed: user not found');\n        throw new BadRequestException('Invalid user');\n      }\n    }\n\n    const allowedAssetIdsSet = await this.checkAccess({\n      auth,\n      permission: Permission.AssetShare,\n      ids: dto.assetIds || [],\n    });\n    const assetIds = [...allowedAssetIdsSet].map((id) => id);\n\n    const userMetadata = await this.userRepository.getMetadata(auth.user.id);\n\n    const album = await this.albumRepository.create(\n      {\n        albumName: dto.albumName,\n        description: dto.description,\n        albumThumbnailAssetId: assetIds[0] || null,\n        order: getPreferences(userMetadata).albums.defaultAssetOrder,","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/services/album.service.ts#L90-L126","documentation":"On album creation, any albumUsers entries other than the owner are checked against the user repository; if a listed userId does not exist, creation is aborted with 'Invalid user'. This prevents creating albums that reference dangling user IDs.","triggerScenarios":"POST /albums with dto.albumUsers containing a userId that is not an existing user (typo, deleted user, or user from another instance).","commonSituations":"Stale client caches listing removed users, copy-pasted UUIDs from another environment, or concurrent user deletion between listing and album creation.","solutions":["Remove the nonexistent userId from albumUsers and retry","Refresh the shareable-user list from the API before creating the album","Verify the userId is a valid UUID belonging to this instance","Handle the 400 at the client and re-prompt the user to select share targets"],"exampleFix":"// before\nconst valid = await Promise.all(ids.map(id => userRepository.get(id, {})));\nawait api.createAlbum({ albumUsers: ids.map(id => ({ userId: id })) });\n// after\nconst existing = (await api.listUsers()).users.map(u => u.id);\nawait api.createAlbum({ albumUsers: ids.filter(id => existing.includes(id)).map(id => ({ userId: id })) });","handlingStrategy":"validation","validationCode":"const validIds = new Set((await api.listUsers()).map(u => u.id));\nconst albumUsers = requested.filter(u => validIds.has(u.userId));","typeGuard":"function userExists(id: string, known: Set<string>): boolean {\n  return known.has(id);\n}","tryCatchPattern":"try {\n  await api.createAlbum(dto);\n} catch (e) {\n  if ((e as Error).message === 'Invalid user') {\n    await refreshUserCache();\n    return retryWithoutInvalidUsers(dto);\n  }\n  throw e;\n}","preventionTips":["Refresh the shareable-users list before creating albums","Exclude the owner's own id from albumUsers (service filters it anyway)","Validate UUIDs client-side before submission","Handle user deletion events by pruning cached share lists"],"tags":["validation","album","user"],"backgroundTag":"user-not-found","analyzedSha":"e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}