{"record":{"id":"864dae4179f5d240","repo":"immich-app/immich","slug":"at-least-two-people-are-required-for-merging","errorCode":null,"errorMessage":"At least two people are required for merging","messagePattern":"At least two people are required for merging","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"server/src/services/person.service.ts","lineNumber":589,"sourceCode":"\n    return JobStatus.Success;\n  }\n\n  @OnJob({ name: JobName.PersonFileMigration, queue: QueueName.Migration })\n  async handlePersonMigration({ ownerId, personGroupId }: JobOf<JobName.PersonFileMigration>): Promise<JobStatus> {\n    const person = await this.personRepository.getByGroupId({ ownerId, personGroupId });\n    if (!person) {\n      return JobStatus.Failed;\n    }\n\n    await this.storageCore.movePersonFile(person, PersonPathType.Face);\n\n    return JobStatus.Success;\n  }\n\n  async mergePeople(auth: AuthDto, { ids }: MergePersonDto): Promise<BulkIdResponseDto[]> {\n    if (ids.length < 2) {\n      throw new BadRequestException('At least two people are required for merging');\n    }\n\n    if (new Set(ids).size !== ids.length) {\n      throw new BadRequestException('Cannot merge a person into themselves');\n    }\n\n    const results: BulkIdResponseDto[] = [];\n\n    const allowedIds = await this.checkAccess({ auth, permission: Permission.PersonMerge, ids });\n\n    const peopleMap: Record<string, Selectable<PersonTable>[]> = {};\n\n    for (const mergePerson of await this.personRepository.getForMergePerson(ids)) {\n      if (!peopleMap[mergePerson.personGroupId]) {\n        peopleMap[mergePerson.personGroupId] = [];\n      }\n      peopleMap[mergePerson.personGroupId].push(mergePerson);\n    }","sourceCodeStart":571,"sourceCodeEnd":607,"githubUrl":"https://github.com/immich-app/immich/blob/f48d4b332127ad365ba256108799ca8f571d2dd5/server/src/services/person.service.ts#L571-L607","documentation":"mergePeople requires at least two person IDs because merging means collapsing one or more source people into a primary. With fewer than two IDs there is nothing to merge, so it throws BadRequestException('At least two people are required for merging') before any access checks.","triggerScenarios":"POST /api/people/{id}/merge (or the merge endpoint) with a MergePersonDto whose ids array has 0 or 1 entries — e.g. only the primary person selected with no sources, or an empty selection.","commonSituations":"UI state where checkboxes were cleared before submitting, API scripts calling merge with a single ID, or client bugs sending ids=[primaryId] only.","solutions":["Include at least two person IDs: the primary person (in the URL) plus at least one source person in the ids array.","Select the duplicate people in the UI before clicking merge.","Fix client scripts to validate ids.length >= 2 before calling the API.","If you only have one person, there is nothing to merge — remove the duplicate expectation from your workflow."],"exampleFix":"// before\nawait api.peopleApi.mergePerson({ id: primaryId, mergePersonDto: { ids: [primaryId] } }); // 1 id -> 400\n// after\nconst ids = [duplicateId1, duplicateId2];\nif (ids.length >= 2) {\n  await api.peopleApi.mergePerson({ id: primaryId, mergePersonDto: { ids } });\n}","handlingStrategy":"validation","validationCode":"if (!Array.isArray(ids) || ids.length < 2) {\n  throw new Error('Select at least two people to merge');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await api.peopleApi.mergePerson({ id: primaryId, mergePersonDto: { ids } });\n} catch (e) {\n  if (e.status === 400 && String(e.message).includes('At least two people are required')) {\n    // re-open selection UI\n  } else throw e;\n}","preventionTips":["Disable the merge button until >= 2 people are selected.","Validate ids.length >= 2 client-side before every merge call.","Remember the primary person goes in the URL, sources go in the body."],"tags":["merge","people","validation","bad-request"],"backgroundTag":"missing-required-argument","analyzedSha":"f48d4b332127ad365ba256108799ca8f571d2dd5","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}