agalwood/Motrix · error · MediaParseError

unsupported-encryption

unsupported-encryption

Error message

AES-128 key missing URI

What it means

MediaParseError with code 'unsupported-encryption', thrown by parseHlsMedia when a #EXT-X-KEY line has METHOD=AES-128 but no URI attribute. AES-128 keys must be fetched from a URI; an AES-128 declaration without one is malformed and there is no key material to decrypt segments.

Source

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

  let pendingByteRange: { length: number; offset?: number } | undefined

  const segments: MediaPart[] = []
  let index = 0

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i] ?? ''

    // EXT-X-KEY
    if (line.startsWith('#EXT-X-KEY:')) {
      const attrs = line.slice('#EXT-X-KEY:'.length)
      const method = attr(attrs, 'METHOD')

      if (method === 'NONE') {
        activeKey = null
      } else if (method === 'AES-128') {
        const keyUri = attr(attrs, 'URI')
        if (!keyUri)
          throw new MediaParseError(
            'unsupported-encryption',
            'AES-128 key missing URI'
          )
        const ivAttr = attr(attrs, 'IV')
        // IV resolution happens per-segment (for seq-based IV)
        // Store a sentinel: if ivAttr is present, store the parsed bytes; else null
        const ivBytes: Uint8Array | null = ivAttr ? parseIvHex(ivAttr) : null
        // We store the IV on activeKey as a placeholder; per-segment we may override below
        // Use a special marker: store ivBytes or the placeholder for seq-based
        activeKey = {
          method: 'AES-128',
          uri: resolveUri(url, keyUri),
          // Temporary: will be resolved per-segment; use seqNumberIv(seq) as default
          iv: ivBytes ?? seqNumberIv(seq),
          _explicit: ivAttr !== undefined,
          _ivBytes: ivBytes,
        } as KeyRef & { _explicit: boolean; _ivBytes: Uint8Array | null }
      } else {

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Inspect the raw playlist and locate the offending #EXT-X-KEY line.
  2. If the URI was stripped by a proxy/rewriter, fix the rewrite rule to preserve URI=.
  3. If the manifest is genuinely malformed, fetch an unmodified copy from the origin.
  4. Pre-validate before parsing: every EXT-X-KEY with METHOD=AES-128 must have a URI attribute.

Example fix

// before — playlist has '#EXT-X-KEY:METHOD=AES-128'
const plan = parseHlsMedia(text, url)
// after — fix the source manifest line to
// '#EXT-X-KEY:METHOD=AES-128,URI="https://cdn.example/key.bin"'
const plan = parseHlsMedia(text, url)
Defensive patterns

Strategy: validation

Validate before calling

function aes128KeysHaveUri(text: string): boolean {
  return [...text.matchAll(/#EXT-X-KEY:[^\n]+/gi)].every(line => {
    const method = /METHOD=([A-Z0-9-]+)/i.exec(line[0])?.[1]
    if (method !== 'AES-128') return true
    return /URI=/i.test(line[0])
  })
}
if (!aes128KeysHaveUri(text)) {
  throw new Error('AES-128 EXT-X-KEY is missing URI.')
}

Type guard

function aes128KeysHaveUri(text: string): boolean {
  return [...text.matchAll(/#EXT-X-KEY:[^\n]+/gi)].every(line => {
    const method = /METHOD=([A-Z0-9-]+)/i.exec(line[0])?.[1]
    return method !== 'AES-128' || /URI=/i.test(line[0])
  })
}

Try / catch

try {
  const plan = parseHlsMedia(text, url)
} catch (e) {
  if (e instanceof MediaParseError && e.code === 'unsupported-encryption' && /missing URI/.test(e.message)) {
    // fetch unmodified manifest from origin
  } else throw e
}

Prevention

When it happens

Trigger: An HLS media playlist containing #EXT-X-KEY:METHOD=AES-128 (no URI=...). Caused by a broken encoder/packager, an in-place edit that stripped the URI, or a manifest proxy that rewrote the line incompletely.

Common situations: Custom manifest filter/rewriter that drops attributes; vendor packager bug; test fixtures hand-written without the URI; copy-paste of an EXT-X-KEY template that left METHOD but removed URI.

Related errors


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