immich-app/immich · error · BadRequestException
Cannot unlink Android motion photos
Error message
Cannot unlink Android motion photos
What it means
Thrown by onBeforeUnlink when unsetting livePhotoVideoId on an asset whose motion video lives on an Android motion-photo path (StorageCore.isAndroidMotionPath). Android motion photos embed their video internally and Immich stores the extracted video under the encoded-video base folder; such motion assets cannot be unlinked because they are managed automatically. HTTP 400.
Source
Thrown at server/src/utils/asset.util.ts:176
}
if (motionAsset && motionAsset.visibility === AssetVisibility.Timeline) {
await assetRepository.update({ id: livePhotoVideoId, visibility: AssetVisibility.Hidden });
await eventRepository.emit('AssetHide', { assetId: motionAsset.id, userId });
}
};
export const onBeforeUnlink = async (
{ asset: assetRepository }: AssetHookRepositories,
{ livePhotoVideoId }: { livePhotoVideoId: string },
) => {
const motion = await assetRepository.getById(livePhotoVideoId);
if (!motion) {
return null;
}
if (StorageCore.isAndroidMotionPath(motion.originalPath)) {
throw new BadRequestException('Cannot unlink Android motion photos');
}
return motion;
};
export const onAfterUnlink = async (
{ asset: assetRepository, event: eventRepository }: AssetHookRepositories,
{ userId, livePhotoVideoId, visibility }: { userId: string; livePhotoVideoId: string; visibility: AssetVisibility },
) => {
await assetRepository.update({ id: livePhotoVideoId, visibility });
await eventRepository.emit('AssetShow', { assetId: livePhotoVideoId, userId });
};
export function mapToUploadFile(file: ImmichFile): UploadFile {
return {
uuid: file.uuid,
checksum: file.checksum,
originalPath: file.path,View on GitHub (pinned to 199723261c)
Solutions
- Do not send livePhotoVideoId=null for assets whose motion component is Android-extracted; let Immich manage them.
- In the client, detect the motion asset's origin (originalPath under encoded-video folder) and skip the unlink call.
- If separation is truly required, delete/re-upload the asset rather than unlinking the embedded motion video.
Example fix
// before
if (asset.livePhotoVideoId) await updateAsset({ id, livePhotoVideoId: null });
// after
const isAndroidMotion = asset.originalPath?.includes('/encoded-video/');
if (asset.livePhotoVideoId && !isAndroidMotion) {
await updateAsset({ id, livePhotoVideoId: null });
} Defensive patterns
Strategy: validation
Validate before calling
import { StorageCore } from 'src/cores/storage.core';
const isAndroidMotion = (p?: string) => !!p && StorageCore.isAndroidMotionPath(p);
// before unlinking, skip Android motion assets
if (asset.livePhotoVideoId && !isAndroidMotion(motionOriginalPath)) { await updateAsset({ id, livePhotoVideoId: null }); } Type guard
const isAndroidMotionAsset = (originalPath?: string): boolean =>
!!originalPath && originalPath.includes('/encoded-video/'); Try / catch
try { await updateAsset({ id, livePhotoVideoId: null }); }
catch (e) { if (/Android motion photos/.test(e.message)) { /* skip silently, managed by Immich */ } else throw e; } Prevention
- Treat Android motion photos as immutable wrt linking; never send livePhotoVideoId=null for them.
- In the client, hide the 'unlink motion' control when the motion asset originates from the encoded-video folder.
When it happens
Trigger: AssetService.update with dto.livePhotoVideoId === null on an asset whose existing motion video originalPath begins with the encoded-video storage folder (i.e. it was extracted from an Android motion photo).
Common situations: User uploads an Android .MP motion photo (JPEG+embedded MP4) and then tries to manually detach the motion video through an asset update; client treats all live photos uniformly and sends unlink for Android ones.
Related errors
- Storage label already in use by another account
- Live photo video not found
- Live photo video must be a video
- Live photo video does not belong to the user
- Quota has been exceeded!
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/a283d219c66ca03f.
Report an issue: GitHub.