{"record":{"id":"0b9273745bb0a97e","repo":"immich-app/immich","slug":"only-images-can-be-edited","errorCode":null,"errorMessage":"Only images can be edited","messagePattern":"Only images can be edited","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"server/src/services/asset.service.ts","lineNumber":536,"sourceCode":"    await this.requireAccess({ auth, permission: Permission.AssetRead, ids: [id] });\n    const edits = await this.assetEditRepository.getAll(id);\n\n    return {\n      assetId: id,\n      edits,\n    };\n  }\n\n  async editAsset(auth: AuthDto, id: string, dto: AssetEditsCreateDto): Promise<AssetEditsResponseDto> {\n    await this.requireAccess({ auth, permission: Permission.AssetEditCreate, ids: [id] });\n\n    const asset = await this.assetRepository.getForEdit(id);\n    if (!asset) {\n      throw new BadRequestException('Asset not found');\n    }\n\n    if (asset.type !== AssetType.Image) {\n      throw new BadRequestException('Only images can be edited');\n    }\n\n    if (asset.livePhotoVideoId) {\n      throw new BadRequestException('Editing live photos is not supported');\n    }\n\n    if (isPanorama(asset)) {\n      throw new BadRequestException('Editing panorama images is not supported');\n    }\n\n    if (asset.originalPath?.toLowerCase().endsWith('.gif')) {\n      throw new BadRequestException('Editing GIF images is not supported');\n    }\n\n    if (asset.originalPath?.toLowerCase().endsWith('.svg')) {\n      throw new BadRequestException('Editing SVG images is not supported');\n    }\n","sourceCodeStart":518,"sourceCodeEnd":554,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/asset.service.ts#L518-L554","documentation":"Thrown by editAsset when the resolved asset's type is not AssetType.Image (e.g. video, audio, or other). The editor only supports raster images, so any other type is rejected with 400 BadRequest before any edit is applied. This is a hard capability gate, independent of file extension.","triggerScenarios":"PUT /assets/{id}/edits on a video, audio file, or any asset whose type !== 'IMAGE'; attempting to route a motion/video asset through the image editor.","commonSituations":"UI lists all assets in one grid and lets the user open the editor on a video; client does not filter by type before enabling the edit action; a live-photo still paired with a video where the wrong record is targeted.","solutions":["Gate the edit button in the UI on asset.type === 'IMAGE'.","Filter the selectable collection to images only before entering the editor.","If you must process a video frame, extract a still image asset first and edit that."],"exampleFix":"// before\nif (asset) openEditor(asset);\n\n// after\nif (asset && asset.type === 'IMAGE' && !asset.livePhotoVideoId) {\n  openEditor(asset);\n}","handlingStrategy":"type-guard","validationCode":"const isEditableImage = (a): a is ImageAsset =>\n  a?.type === 'IMAGE' && !a.livePhotoVideoId;\nif (!isEditableImage(asset)) {\n  throw new Error('Only still images can be edited.');\n}","typeGuard":"function isEditableImage(asset: unknown): asset is { type: 'IMAGE'; id: string } {\n  return !!asset && typeof asset === 'object' && (asset as any).type === 'IMAGE';\n}","tryCatchPattern":null,"preventionTips":["Filter the editor-eligible collection to assets where type === 'IMAGE'.","Disable the edit affordance on videos/audio in the UI.","Unit-test the type predicate against mixed-type lists."],"tags":["asset","edit","validation","type-check","image"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}