{"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":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","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/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/services/stack.service.ts#L23-L59","documentation":"Immich throws this BadRequestException in StackService.update when the requested primaryAssetId is not one of the assets belonging to the stack. The primary asset must be a member of the stack being updated.","triggerScenarios":"PUT /stacks/:id with a primaryAssetId that references an asset not in the stack (wrong stack id, asset unstacked earlier, or an id from a different stack).","commonSituations":"Clients caching asset ids then the stack was modified concurrently; UI sending the currently-viewed asset's id instead of one inside the stack; bulk scripts pairing stack ids and asset ids from different sources.","solutions":["Fetch the stack (GET /stacks/:id) and pick primaryAssetId from its assets array.","Verify the asset's stackId matches the stack before updating.","If the asset should join the stack first, add it to the stack, then set it as primary.","Refresh client-side stack data before retrying to clear stale ids."],"exampleFix":"// before\nawait api.updateStack(auth, stackId, { primaryAssetId: someAssetId });\n// after\nconst stack = await api.getStack(auth, stackId);\nif (stack.assets.some(a => a.id === someAssetId)) {\n  await api.updateStack(auth, stackId, { primaryAssetId: someAssetId });\n}","handlingStrategy":"validation","validationCode":"const stack = await api.getStack(auth, stackId);\nif (!stack.assets.some(a => a.id === primaryAssetId)) throw new Error('primaryAssetId must be a member of the stack');","typeGuard":"const isInStack = (stack: { assets: { id: string }[] }, assetId: string) =>\n  stack.assets.some(a => a.id === assetId);","tryCatchPattern":"try {\n  await api.updateStack(auth, stackId, { primaryAssetId });\n} catch (e) {\n  if (e.status === 400 && /Primary asset must be in the stack/.test(e.message)) {\n    // refetch stack, pick a valid asset id, retry\n  } else throw e;\n}","preventionTips":["Always pick primaryAssetId from the stack's assets array","Refetch stack data before updates to avoid stale ids","Validate stack membership in UI before submitting","Watch for concurrent unstack operations"],"tags":["immich","stacks","bad-request","validation"],"backgroundTag":"invalid-argument-value","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"}