{"record":{"id":"de50fa3fb471cf54","repo":"immich-app/immich","slug":"asset-not-in-stack","errorCode":null,"errorMessage":"Asset not in stack","messagePattern":"Asset not in stack","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/stack.service.ts","lineNumber":70,"sourceCode":"    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);\n    await this.eventRepository.emit('StackDeleteAll', { stackIds: dto.ids, userId: auth.user.id });\n  }\n\n  async removeAsset(auth: AuthDto, dto: UUIDAssetIDParamDto): Promise<void> {\n    const { id: stackId, assetId } = dto;\n    await this.requireAccess({ auth, permission: Permission.StackUpdate, ids: [stackId] });\n\n    const stack = await this.stackRepository.getForAssetRemoval(assetId);\n\n    if (!stack?.id || stack.id !== stackId) {\n      throw new BadRequestException('Asset not in stack');\n    }\n\n    if (stack.primaryAssetId === assetId) {\n      throw new BadRequestException(\"Cannot remove stack's primary asset\");\n    }\n\n    await this.assetRepository.update({ id: assetId, stackId: null });\n    await this.eventRepository.emit('StackUpdate', { stackId, userId: auth.user.id });\n  }\n\n  private findOrFail(id: string) {\n    return findOrFail(() => this.stackRepository.getById(id), 'Asset stack');\n  }\n}\n","sourceCodeStart":52,"sourceCodeEnd":85,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/services/stack.service.ts#L52-L85","documentation":"Immich throws this BadRequestException when attempting to remove an asset from a stack that does not actually contain it — the stack fetched for the asset (getForAssetRemoval) is missing or its id differs from the target stackId in the request.","triggerScenarios":"DELETE /stacks/:id/assets with an assetId whose actual stack differs from the :id path parameter, the asset is not stacked at all, or ids are swapped/mistyped.","commonSituations":"Client using a stale stack id after the asset was moved to another stack; passing (stackId, assetId) in the wrong order; concurrent unstacking by another client or job.","solutions":["Confirm the asset is stacked and note its actual stackId (GET /asset/:id or asset.stackedAssets).","Call the endpoint with the stack id that actually contains the asset.","Refresh stack data before retrying if another client may have changed stacking.","Check argument order — dto takes { id (stack), assetId } — and ensure they aren't swapped."],"exampleFix":"// before\nawait api.removeAssetFromStack(auth, oldStackId, assetId);\n// after\nconst asset = await api.getAssetInfo(auth, assetId);\nconst actualStackId = asset.stack?.id ?? asset.stackId;\nawait api.removeAssetFromStack(auth, actualStackId, assetId);","handlingStrategy":"validation","validationCode":"const asset = await api.getAssetInfo(auth, assetId);\nif (asset.stackId !== stackId) throw new Error(`Asset ${assetId} is not in stack ${stackId}`);","typeGuard":"const assetIsInStack = (asset: { stackId?: string | null }, stackId: string) => asset.stackId === stackId;","tryCatchPattern":"try {\n  await api.removeAssetFromStack(auth, stackId, assetId);\n} catch (e) {\n  if (e.status === 400 && /Asset not in stack/.test(e.message)) {\n    // refetch asset to get its real stackId, then retry\n  } else throw e;\n}","preventionTips":["Resolve the asset's actual stackId from the asset record before calling","Avoid caching (stackId, assetId) pairs across sessions","Refresh stack state after any stacking operation","Check dto argument order { id, assetId }"],"tags":["immich","stacks","bad-request","state-mismatch"],"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"}