immich-app/immich · error · NotFoundException

No supported variants for this video

Error message

No supported variants for this video

What it means

generateMainPlaylist builds one EXT-X-STREAM-INF line per transcoding resolution the asset supports; if none could be added (lines.length === 4 means only the header lines remain), it throws 404 NotFoundException 'No supported variants for this video'. This happens when the asset's video codec/stream is not compatible with any configured output variant, or the codec string/resolution computation rejected every option.

Source

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

    const targetResolution = Math.max(sourceResolution, HLS_VARIANTS[0].resolution);
    const lines = ['#EXTM3U', `#EXT-X-VERSION:${HLS_VERSION}`, '#EXT-X-INDEPENDENT-SEGMENTS'];
    const { videoCodecs, resolutions } = ffmpeg.realtime;
    for (let i = 0; i < HLS_VARIANTS.length; i++) {
      const { resolution, bitrate, codec } = HLS_VARIANTS[i];
      if (resolution > targetResolution || !videoCodecs.includes(codec) || !resolutions.includes(resolution)) {
        continue;
      }
      const { width, height } = getOutputSize(asset.videoStream, resolution);
      const codecString = getCodecString(codec, width, height, fps);
      lines.push(
        `#EXT-X-STREAM-INF:BANDWIDTH=${Math.round(bitrate * 1.35)},RESOLUTION=${width}x${height},CODECS="${codecString},mp4a.40.2",VIDEO-RANGE=SDR,FRAME-RATE=${roundedFps}`,
        `${sessionId}/${i}/playlist.m3u8`,
      );
    }
    lines.push('');

    if (lines.length === 4) {
      throw new NotFoundException('No supported variants for this video');
    }

    return lines.join('\n');
  }

  private getSegmentation({ videoStream, packets }: AssetWithStreamInfo): Segmentation {
    const fps = (packets.packetCount * videoStream.timeBase) / packets.totalDuration;
    const framesPerSegment = Math.ceil(HLS_SEGMENT_DURATION * fps);
    const segmentCount = Math.ceil(packets.outputFrames / framesPerSegment);
    return { fps, framesPerSegment, segmentCount, segmentDuration: framesPerSegment / fps };
  }

  private positionToSegmentIndex({ segmentDuration, segmentCount }: Segmentation, position: number) {
    return Math.min(Math.max(Math.floor(position / segmentDuration), 0), segmentCount - 1);
  }

  private generateMediaPlaylist({ packets }: AssetWithStreamInfo, segmentation: Segmentation) {
    const { fps, framesPerSegment, segmentCount, segmentDuration: fullSegmentDuration } = segmentation;

View on GitHub (pinned to 199723261c)

Solutions

  1. Review System Settings -> Video Encoding and ensure at least one targetVideoCodec is enabled that the source can be transcoded to (commonly h264).
  2. Confirm a transcode target resolution is enabled (CONVERTED_VIDEO_FORMATS / desired resolutions).
  3. Check the asset's actual codec via the asset metadata and install/enable the decoder (e.g. enable HEVC support in ffmpeg).
  4. As a last resort, transcode the source file externally and re-upload it in a supported format.
Defensive patterns

Strategy: try-catch

Validate before calling

// check the asset's codec is supported by configured transcode targets before offering HLS
const cfg = await api.systemConfigApi.getConfig();
const supportedCodecs = cfg.ffmpeg.targetVideoCodec; // e.g. ['h264','hevc']
const asset = await api.assetApi.get(assetId);
if (!supportedCodecs.includes(asset.imageCodec /* or videoCodec */)) {
  warnUnsupportedCodec(asset);
}

Type guard

const hasSupportedVariant = (asset: { videoCodec?: string }, targets: string[]) =>
  !!asset.videoCodec && targets.includes(asset.videoCodec);

Try / catch

try {
  await api.hlsApi.getMainPlaylist(auth, assetId);
} catch (e) {
  if (e.status === 404 && /No supported variants/.test(e.message)) {
    showFallback('Direct download only - no streaming variants available for this codec');
  } else throw e;
}

Prevention

When it happens

Trigger: getMainPlaylist proceeds, asset metadata exists, but every variant was skipped in the loop (e.g. continue due to codec/targetVideoCodec mismatch, an exotic codec like HEVC with no transcode target enabled, or all resolutions higher/lower than configured). Result: no stream-INF lines.

Common situations: Video in a codec the configured transcode stack cannot decode; targetVideoCodec misconfigured; all transcode resolutions disabled; very unusual container/codec combo.

Related errors


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