immich-app/immich · error · BadRequestException

Live photo video not found

Error message

Live photo video not found

What it means

Thrown by onBeforeLink in server/src/utils/asset.util.ts when a user attempts to link a Live Photo's still image to a motion video asset, but no asset exists in the database for the supplied livePhotoVideoId. It is a NestJS BadRequestException (HTTP 400), surfaced through asset update calls. The lookup is done via assetRepository.getById(livePhotoVideoId).

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 199723261c)

Solutions

  1. Verify the motion video asset was uploaded successfully and capture its real id before linking it as livePhotoVideoId.
  2. Confirm livePhotoVideoId belongs to the same user and still exists (e.g. GET /assets/:id) before submitting the link request.
  3. If the asset was deleted, re-upload the motion video and use the new id.

Example fix

// before
await updateAsset({ id, livePhotoVideoId: someId }); // someId may be stale

// after
const motion = await getAsset(motionId);
if (!motion) { throw new Error('motion asset missing, re-upload'); }
await updateAsset({ id, livePhotoVideoId: motion.id });
Defensive patterns

Strategy: validation

Validate before calling

// before linking, confirm the motion asset exists for this user
const motion = await assetRepository.getById(livePhotoVideoId);
if (!motion) { /* surface a friendly error instead of throwing deep in onBeforeLink */ }

Type guard

// runtime guard for the link payload
const isLivePhotoLinkDto = (v: any): v is { livePhotoVideoId: string } =>
  typeof v?.livePhotoVideoId === 'string' && v.livePhotoVideoId.length > 0;

Try / catch

// catch BadRequestException around the update call and map 'Live photo video not found' to a retry-upload UX
try { await updateAsset({ id, livePhotoVideoId }); }
catch (e) { if (e instanceof BadRequestException && /Live photo video not found/.test(e.message)) { await reuploadMotionVideo(); } else throw e; }

Prevention

When it happens

Trigger: PUT/PATCH /assets/:id (AssetService.update) with dto.livePhotoVideoId set to a value that does not match any asset row, or POST /assets (asset-media upload) passing a livePhotoVideoId that was never persisted (e.g. the motion-video upload failed or returned an id the client fabricated).

Common situations: Client sends a stale or fabricated livePhotoVideoId after the companion motion video upload failed/timed out; race where the motion asset was deleted between upload and link; test/seed data referencing an id that no longer exists.

Related errors


AI-assisted analysis of immich-app/immich@199723261c (2026-08-12). Data as JSON: /api/errors/1df72b73bfb5a5a7. Report an issue: GitHub.