agalwood/Motrix · error · MediaParseError
unsupported-master
unsupported-master
Error message
master playlist has no variants
What it means
MediaParseError with code 'unsupported-master', thrown by parseHlsMaster when no #EXT-X-STREAM-INF:bgtag line with a following non-comment URI line was found. A master playlist must enumerate at least one variant; otherwise there is nothing to play and the master is malformed or it is actually a media playlist fed to the wrong parser.
Source
Thrown at src/core/media/hls-parser.ts:98
const variants: Variant[] = []
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
if (!line?.startsWith('#EXT-X-STREAM-INF:')) continue
const attrs = line.slice('#EXT-X-STREAM-INF:'.length)
const bandwidth = Number(attr(attrs, 'BANDWIDTH') ?? '0')
const audioGroup = attr(attrs, 'AUDIO')
const uriLine = lines[i + 1]?.trim()
if (uriLine && !uriLine.startsWith('#')) {
variants.push({
url: resolveUri(url, uriLine),
bandwidth,
audioGroup,
})
}
}
if (variants.length === 0) {
throw new MediaParseError(
'unsupported-master',
'master playlist has no variants'
)
}
const best = variants.reduce((a, b) => (b.bandwidth > a.bandwidth ? b : a))
const audioUrl =
best.audioGroup != null ? audioRenditions.get(best.audioGroup) : undefined
return { variantUrl: best.url, ...(audioUrl ? { audioUrl } : {}) }
}
// ---------------------------------------------------------------------------
// parseHlsMedia
// ---------------------------------------------------------------------------
/**
* Parse a media (non-master) HLS playlist into a SegmentPlan.View on GitHub (pinned to 1a708ee577)
Solutions
- Confirm the URL points at a master playlist (contains #EXT-X-STREAM-INF).
- If the URL is actually a media playlist, call parseHlsMedia instead.
- Auto-detect: if text.includes('#EXT-X-STREAM-INF') use parseHlsMaster, else if text.includes('#EXTINF') use parseHlsMedia.
- Inspect the raw playlist text to ensure each #EXT-X-STREAM-INF has a URI on the immediately following non-blank, non-# line.
Example fix
// before
const { variantUrl } = parseHlsMaster(text, url)
// after — detect playlist role first
if (text.includes('#EXT-X-STREAM-INF')) {
const { variantUrl } = parseHlsMaster(text, url)
} else if (text.includes('#EXTINF')) {
const plan = parseHlsMedia(text, url)
} else {
throw new Error('Unrecognised HLS playlist shape')
} Defensive patterns
Strategy: validation
Validate before calling
function isHlsMasterPlaylist(text: string): boolean {
return text.includes('#EXT-X-STREAM-INF')
}
if (!isHlsMasterPlaylist(text)) {
if (text.includes('#EXTINF')) {
// call parseHlsMedia instead
} else {
throw new Error('Text is not a recognised HLS playlist.')
}
} Type guard
function isHlsMasterPlaylist(text: string): boolean {
return text.includes('#EXT-X-STREAM-INF')
} Try / catch
try {
const { variantUrl } = parseHlsMaster(text, url)
} catch (e) {
if (e instanceof MediaParseError && e.code === 'unsupported-master') {
if (text.includes('#EXTINF')) {
// it was actually a media playlist — call parseHlsMedia
}
} else throw e
} Prevention
- Detect playlist role (#EXT-X-STREAM-INF vs #EXTINF) before dispatching.
- Validate that each #EXT-X-STREAM-INF has a following URI line when ingesting user URLs.
- Keep URL-to-role metadata if you control the asset catalogue.
When it happens
Trigger: Calling parseHlsMaster on a media (non-master) playlist that contains #EXTINF segments instead of #EXT-X-STREAM-INF; on a master whose variant URI lines are missing or all commented; on an empty or stub playlist; on a playlist whose #EXT-X-STREAM-INF line is the last line (no following URI).
Common situations: URL-dispatch bug: a media playlist URL handed to the master parser; playlists generated by a misconfigured encoder that emitted the header but no variants; trailing-newline issues where the URI line is consumed by line-splitting.
Related errors
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/878f473e4742d95a.
Report an issue: GitHub.