agalwood/Motrix · error · MediaParseError

unsupported-live

unsupported-live

Error message

live HLS is not supported

What it means

MediaParseError with code 'unsupported-live', thrown by parseHlsMedia when the playlist has neither #EXT-X-ENDLIST nor #EXT-X-PLAYLIST-TYPE:VOD. The library only handles static (complete) HLS media playlists because it builds a fixed SegmentPlan; live/sliding-window playlists require ongoing refresh which it does not model.

Source

Thrown at src/core/media/hls-parser.ts:139

 *  - METHOD=NONE → clears active key
 *  - METHOD=SAMPLE-AES/other → MediaParseError('unsupported-encryption')
 *  - EXT-X-BYTERANGE with running per-resource offset
 *  - Live guard: no ENDLIST and not PLAYLIST-TYPE:VOD → MediaParseError('unsupported-live')
 */
export function parseHlsMedia(text: string, url: string): SegmentPlan {
  const lines = text.split(/\r?\n/)

  const hasEndlist = lines.some((l) => l.startsWith('#EXT-X-ENDLIST'))
  const playlistType = lines
    .find((l) => l.startsWith('#EXT-X-PLAYLIST-TYPE:'))
    ?.slice('#EXT-X-PLAYLIST-TYPE:'.length)
    ?.trim()
    ?.toUpperCase()

  const isVod = playlistType === 'VOD'

  if (!hasEndlist && !isVod) {
    throw new MediaParseError('unsupported-live', 'live HLS is not supported')
  }

  // Parse MEDIA-SEQUENCE
  let seq = 0
  const seqLine = lines.find((l) => l.startsWith('#EXT-X-MEDIA-SEQUENCE:'))
  if (seqLine) {
    seq = Number(seqLine.slice('#EXT-X-MEDIA-SEQUENCE:'.length).trim())
  }

  // Running state
  let activeKey: KeyRef | null = null
  let activeInit: InitSegment | undefined
  // Per-resource running byte offset (keyed by resolved URL)
  const runningOffset = new Map<string, number>()
  // Pending BYTERANGE for the next segment
  let pendingByteRange: { length: number; offset?: number } | undefined

  const segments: MediaPart[] = []

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Point the fetcher at a finished (ENDLIST present) VOD asset URL.
  2. If you must play live, use a player with native live HLS support (hls.js, AVPlayer, ExoPlayer) instead of this parser.
  3. Pre-check: if (!text.includes('#EXT-X-ENDLIST') && !/PLAYLIST-TYPE:VOD/i.test(text)) reject as live before calling parseHlsMedia.
  4. For event streams, wait until recording completes and the ENDLIST is appended, then re-fetch.

Example fix

// before
const plan = parseHlsMedia(text, url)
// after
const isLive = !text.includes('#EXT-X-ENDLIST') && !/#EXT-X-PLAYLIST-TYPE:VOD/i.test(text)
if (isLive) throw new UserFacingError('Live HLS is not supported. Use a VOD URL.')
const plan = parseHlsMedia(text, url)
Defensive patterns

Strategy: validation

Validate before calling

function isStaticHls(text: string): boolean {
  return text.includes('#EXT-X-ENDLIST') || /#EXT-X-PLAYLIST-TYPE:VOD/i.test(text)
}
if (!isStaticHls(text)) {
  throw new Error('Live HLS is not supported; provide a VOD URL.')
}

Type guard

function isStaticHls(text: string): boolean {
  return text.includes('#EXT-X-ENDLIST') || /#EXT-X-PLAYLIST-TYPE:VOD/i.test(text)
}

Try / catch

try {
  const plan = parseHlsMedia(text, url)
} catch (e) {
  if (e instanceof MediaParseError && e.code === 'unsupported-live') {
    // prompt user for a VOD URL
  } else throw e
}

Prevention

When it happens

Trigger: Calling parseHlsMedia on an active live HLS playlist (no EXT-X-ENDLIST, no PLAYLIST-TYPE:VOD). Typical for ongoing event streams, 24/7 linear channels, or playlists where the encoder has not yet written the closing tag.

Common situations: User-supplied live URL; event playlists still being recorded; CDN edge that strips EXT-X-ENDLIST on in-progress assets; testing against Apple's bipbop live samples rather than the VOD ones.

Related errors


AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12). Data as JSON: /api/errors/1a9e22a9e8a0c275. Report an issue: GitHub.