immich-app/immich · error · BadRequestException

Live photo video not found

Error message

Live photo video not found

What it means

Thrown by onBeforeLink when linking a live photo's motion (video) component to an asset, but no asset exists for the provided livePhotoVideoId. Immich requires the motion video to be an existing asset before it can be attached to the image asset. This prevents dangling references to non-existent assets.

Solutions

  1. Verify the livePhotoVideoId references an existing asset via GET /api/assets/:id
  2. Re-upload the missing motion video component and retry the link
  3. Check the client is pointing at the correct server (asset IDs are not portable across instances)
  4. Clear stale client-side cache of live photo pairs and re-pair assets

Example fix

// before
await onBeforeLink(repos, { userId, livePhotoVideoId: staleId });
// after
const motion = await assetRepository.getById(staleId);
if (!motion) {
  console.warn(`Motion asset ${staleId} missing; re-uploading before link`);
  const fresh = await uploadMotionVideo(pair);
  await onBeforeLink(repos, { userId, livePhotoVideoId: fresh.id });
}
Defensive patterns

Strategy: validation

Validate before calling

const motion = await api.getAsset(livePhotoVideoId);
if (!motion) throw new Error(`Motion asset ${livePhotoVideoId} not found; re-upload before linking`);

Try / catch

try {
  await linkLivePhoto(assetId, livePhotoVideoId);
} catch (e) {
  if (e.status === 400 && /not found/.test(e.message)) {
    // re-upload motion video and retry link
  }
}

Prevention

When it happens

Trigger: Calling asset link (onBeforeLink) with a livePhotoVideoId that was deleted, never uploaded, or belongs to a different Immich server/instance.

Common situations: Mobile client uploads a live photo pair where the video part failed to upload; user deleted the motion video but the client still references its ID; syncing clients with stale IDs after a database restore.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15). Data as JSON: /api/errors/1df72b73bfb5a5a7. Report an issue: GitHub.

Appendix: source

Thrown at server/src/utils/asset.util.ts:151

    if (timelineEnabled && !partner.inTimeline) {
      continue;
    }

    partnerIds.add(partner.sharedById);
  }

  return [...partnerIds];
};

export type AssetHookRepositories = { asset: AssetRepository; event: EventRepository };

export const onBeforeLink = async (
  { asset: assetRepository, event: eventRepository }: AssetHookRepositories,
  { userId, livePhotoVideoId }: { userId: string; livePhotoVideoId: string },
) => {
  const motionAsset = await assetRepository.getById(livePhotoVideoId);
  if (!motionAsset) {
    throw new BadRequestException('Live photo video not found');
  }
  if (motionAsset.type !== AssetType.Video) {
    throw new BadRequestException('Live photo video must be a video');
  }
  if (motionAsset.ownerId !== userId) {
    throw new BadRequestException('Live photo video does not belong to the user');
  }

  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 },
) => {

View on GitHub (pinned to e55ac299a4)