{"record":{"id":"e1999cd0dcdf28ae","repo":"immich-app/immich","slug":"source-and-target-id-must-be-distinct","errorCode":null,"errorMessage":"Source and target id must be distinct","messagePattern":"Source and target id must be distinct","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"server/src/services/asset.service.ts","lineNumber":204,"sourceCode":"      sourceId,\n      targetId,\n      albums = true,\n      sidecar = true,\n      sharedLinks = true,\n      stack = true,\n      favorite = true,\n    }: AssetCopyDto,\n  ) {\n    await this.requireAccess({ auth, permission: Permission.AssetCopy, ids: [sourceId, targetId] });\n    const sourceAsset = await this.assetRepository.getForCopy(sourceId);\n    const targetAsset = await this.assetRepository.getForCopy(targetId);\n\n    if (!sourceAsset || !targetAsset) {\n      throw new BadRequestException('Both assets must exist');\n    }\n\n    if (sourceId === targetId) {\n      throw new BadRequestException('Source and target id must be distinct');\n    }\n\n    if (albums) {\n      await this.albumRepository.copyAlbums({ sourceAssetId: sourceId, targetAssetId: targetId });\n    }\n\n    if (sharedLinks) {\n      await this.sharedLinkAssetRepository.copySharedLinks({ sourceAssetId: sourceId, targetAssetId: targetId });\n    }\n\n    if (stack) {\n      await this.copyStack({ sourceAsset, targetAsset });\n    }\n\n    if (favorite) {\n      await this.assetRepository.update({ id: targetId, isFavorite: sourceAsset.isFavorite });\n    }\n","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/asset.service.ts#L186-L222","documentation":"Thrown by AssetService.copyAssetMetadata when sourceId === targetId. Copying an asset's metadata onto itself is a no-op and is rejected explicitly after the existence check passes. BadRequestException (HTTP 400).","triggerScenarios":"POST /assets/copy where the source and target id are the same value — e.g. a UI bug that passes the same selected asset as both source and target, or a copy-to-self feature attempt.","commonSituations":"Drag-and-drop copy where the drop target resolves to the source; batch copy where a source id leaks into the target list; testing copy with the same id for simplicity.","solutions":["Filter the target list to exclude the source id before calling copy","Add a UI guard that disables 'copy to self'","Validate sourceId !== targetId client-side before the request"],"exampleFix":"// before\nawait sdk.copyAsset(id, id, opts); // 400\n// after\nif (sourceId === targetId) return; // skip self\nawait sdk.copyAsset(sourceId, targetId, opts);","handlingStrategy":"validation","validationCode":"// Exclude self from copy targets\nif (sourceId === targetId) {\n  throw new Error('source and target must differ');\n}\nawait sdk.copyAsset(sourceId, targetId, opts);\n\n// For bulk targets:\nconst targets = allTargetIds.filter(id => id !== sourceId);","typeGuard":"function areDistinct(a: string, b: string): boolean {\n  return a !== b;\n}","tryCatchPattern":null,"preventionTips":["Filter target lists to exclude the source id before calling copy","Disable 'copy to self' in the UI","Validate sourceId !== targetId client-side and skip silently if equal"],"tags":["asset","copy","validation","nestjs"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}