{"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":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/asset.service.ts","lineNumber":541,"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":523,"sourceCodeEnd":559,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/services/asset.service.ts#L523-L559","documentation":"editAsset only supports editing images; after confirming the asset exists it checks asset.type !== AssetType.Image and throws BadRequestException('Only images can be edited'). Videos and other non-image asset types are rejected by design.","triggerScenarios":"Submitting an edit (crop/rotate/etc.) for an asset whose type is Video, Motion photo video component, or any non-Image AssetType, e.g. PUT edits on a video id.","commonSituations":"Bulk edit scripts iterating a playlist or album that includes videos; clients applying UI-edited transforms to all assets in a selection without filtering type; misidentifying a motion photo's video part as the editable asset.","solutions":["Filter assets to type === 'IMAGE' (AssetType.Image) before submitting edits","Skip videos in bulk edit flows with a clear user-facing skip reason","For videos, use video-specific tooling instead of the image edit API","Check the asset's type field from GET /api/assets/:id before editing"],"exampleFix":"// before\nfor (const a of selected) await applyEdits(a.id, edits);\n// after\nfor (const a of selected.filter((a) => a.type === 'IMAGE')) await applyEdits(a.id, edits);","handlingStrategy":"validation","validationCode":"if (asset.type !== 'IMAGE') throw new Error(`Asset ${asset.id} is ${asset.type}; only IMAGE assets can be edited`);","typeGuard":"const isEditableImage = (a: { type: string }) => a.type === 'IMAGE';","tryCatchPattern":"try { await api.applyEdits(id, edits); } catch (e) { if (String(e?.response?.data?.message) === 'Only images can be edited') { markUnsupported(id); return; } throw e; }","preventionTips":["Filter selections to type 'IMAGE' before enabling edit actions","Hide edit controls for video assets in the UI","Skip non-images in bulk edit scripts and log them","Re-check asset.type from the API, not client-side guesses"],"tags":["asset","edit","unsupported-type","validation"],"backgroundTag":"unsupported-operation","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"}