immich-app/immich · error · NotFoundException

Asset not found or metadata not yet ready for streaming

Error message

Asset not found or metadata not yet ready for streaming

What it means

Guard when opening an HLS stream: the asset's transcode metadata (video stream info, segmentation) has not been generated yet by the background metadata pipeline, or the asset id does not exist, so playback cannot start. Raised in getMediaPlaylist as a 404 before requesting a transcode session from the microservices worker. Fires for brand-new uploads whose metadata is still processing or stale/deleted asset ids.

Solutions

  1. Poll/retry after a short delay until asset metadata extraction completes
  2. Verify the assetId exists before requesting the playlist
  3. Show the user a 'video is still processing' state instead of the player
  4. Check that the metadata/transcode worker service is running and healthy
Defensive patterns

Strategy: retry

When it happens

Trigger: Thrown at server/src/services/hls.service.ts:74 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at server/src/services/hls.service.ts:74

      throw new NotFoundException('Asset metadata is not yet ready for streaming');
    }

    // Sharing the sessionId allows only one microservices worker to successfully insert to the session table.
    // The microservices worker that creates a session owns the transcoding lifecycle for it.
    const sessionId = this.cryptoRepository.randomUUID();
    this.websocketRepository.serverSend('HlsSessionRequest', { sessionId, assetId, ownerId: auth.user.id });
    await this.pendingSessions.wait(sessionId);
    this.trackSession(sessionId);

    return this.generateMainPlaylist(sessionId, ffmpeg, asset);
  }

  async getMediaPlaylist(auth: AuthDto, assetId: string, sessionId: string, variantIndex: number, position?: number) {
    await this.requireAccess({ auth, permission: Permission.AssetView, ids: [assetId] });

    const asset = await this.videoStreamRepository.getForMediaPlaylist(assetId, sessionId);
    if (!asset) {
      throw new NotFoundException('Asset not found or metadata not yet ready for streaming');
    }

    const segmentation = this.getSegmentation(asset);
    const hintedSegment = position === undefined ? undefined : this.positionToSegmentIndex(segmentation, position);
    this.prewarmVariant(assetId, sessionId, variantIndex, hintedSegment);

    return this.generateMediaPlaylist(asset, segmentation);
  }

  async getSegment(
    auth: AuthDto,
    assetId: string,
    sessionId: string,
    variantIndex: number,
    filename: string,
    initSegment?: number,
  ) {
    await this.requireAccess({ auth, permission: Permission.AssetView, ids: [assetId] });

View on GitHub (pinned to e55ac299a4)