{"record":{"id":"637b04b69ec7a0f0","repo":"immich-app/immich","slug":"primary-asset-must-be-in-the-stack","errorCode":null,"errorMessage":"Primary asset must be in the stack","messagePattern":"Primary asset must be in the stack","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"server/src/services/stack.service.ts","lineNumber":41,"sourceCode":"\n    const stack = await this.stackRepository.create({ ownerId: auth.user.id }, dto.assetIds);\n\n    await this.eventRepository.emit('StackCreate', { stackId: stack.id, userId: auth.user.id });\n\n    return mapStack(stack, { auth });\n  }\n\n  async get(auth: AuthDto, id: string): Promise<StackResponseDto> {\n    await this.requireAccess({ auth, permission: Permission.StackRead, ids: [id] });\n    const stack = await this.findOrFail(id);\n    return mapStack(stack, { auth });\n  }\n\n  async update(auth: AuthDto, id: string, dto: StackUpdateDto): Promise<StackResponseDto> {\n    await this.requireAccess({ auth, permission: Permission.StackUpdate, ids: [id] });\n    const stack = await this.findOrFail(id);\n    if (dto.primaryAssetId && stack.assets.every(({ id }) => id !== dto.primaryAssetId)) {\n      throw new BadRequestException('Primary asset must be in the stack');\n    }\n\n    const updatedStack = await this.stackRepository.update(id, { id, primaryAssetId: dto.primaryAssetId });\n\n    await this.eventRepository.emit('StackUpdate', { stackId: id, userId: auth.user.id });\n\n    return mapStack(updatedStack, { auth });\n  }\n\n  async delete(auth: AuthDto, id: string): Promise<void> {\n    await this.requireAccess({ auth, permission: Permission.StackDelete, ids: [id] });\n    await this.stackRepository.delete(id);\n    await this.eventRepository.emit('StackDelete', { stackId: id, userId: auth.user.id });\n  }\n\n  async deleteAll(auth: AuthDto, dto: BulkIdsDto): Promise<void> {\n    await this.requireAccess({ auth, permission: Permission.StackDelete, ids: dto.ids });\n    await this.stackRepository.deleteAll(dto.ids);","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/stack.service.ts#L23-L59","documentation":"StackService.update() lets you change a stack's primary asset, but the new primaryAssetId must already be a member of that stack. If dto.primaryAssetId is set AND none of stack.assets has that id, it throws BadRequestException 'Primary asset must be in the stack' (stack.service.ts:41, HTTP 400).","triggerScenarios":"PATCH /stacks/:id with a primaryAssetId that is not one of the stack's current asset ids. The check runs after the StackUpdate permission check.","commonSituations":"Setting primary to an asset id from a different stack, a stale client view of stack membership after assets were added/removed, or passing an asset id that was deleted.","solutions":["Ensure the primaryAssetId you send is currently in the stack (fetch GET /stacks/:id first).","If you need to promote an external asset, add it to the stack before setting it as primary.","Refresh the client's stack membership view before issuing the update."],"exampleFix":"// before - asset not in stack\nawait update(stackId, { primaryAssetId: externalAssetId });\n// after - promote an existing member\nawait update(stackId, { primaryAssetId: stack.assets[0].id });","handlingStrategy":"validation","validationCode":"const stack = await stackApi.get(id);\nconst memberIds = new Set(stack.assets.map((a) => a.id));\nif (dto.primaryAssetId && !memberIds.has(dto.primaryAssetId)) {\n  throw new Error('primaryAssetId must be a current member of the stack.');\n}\nawait stackApi.update(id, dto);","typeGuard":"const isStackMember = (id: string, stack: { assets: { id: string }[] }): boolean =>\n  stack.assets.some((a) => a.id === id);","tryCatchPattern":"try {\n  await stackApi.update(id, dto);\n} catch (e) {\n  if (e instanceof BadRequestException && /must be in the stack/i.test(e.message)) {\n    refreshStackMembership(id);\n  } else throw e;\n}","preventionTips":["Fetch current stack membership before promoting a primary.","Add external assets to the stack before setting them as primary."],"tags":["stack","asset","validation"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}