Skip to content

yt-dlp "Requested format is not available" — Live Streams Serve an HLS-Only Ladder

Problem

A URL that would work fine as a normal video fails immediately, before any download starts:

[youtube] p03cQjZn71A: Downloading visionos player API JSON
[youtube] p03cQjZn71A: Downloading m3u8 information
ERROR: [youtube] p03cQjZn71A: Requested format is not available.
Use --list-formats for a list of available formats

Two details make this look like the PO-token / client-blocking family of failures documented elsewhere on this wiki, and it is neither:

  • Only the visionos client returned anything. tv, web_embedded, mweb and web were all contacted and all came back with zero formats.
  • Every returned format is m3u8. There is not a single https (DASH) rung.

The tell is that this is a format-selection error, not a network error. yt-dlp reached YouTube, got a manifest, sorted the formats, and then found nothing matching the requested selector. Nothing was ever fetched.

Cause

The video is live right now. Confirm it directly rather than inferring:

yt-dlp --simulate --print "live_status=%(live_status)s duration=%(duration_string)s" <URL>
# live_status=is_live duration=NA

A live YouTube broadcast is served only as HLS. There is no DASH manifest until the broadcast ends and YouTube finishes processing it into a VOD. The HLS ladder differs from the VOD ladder in three ways that matter to a format selector:

VOD (DASH) Live (HLS)
Audio acodec mp4a.40.2 unknown
Audio ext m4a mp4
Combined video+audio rung usually present (e.g. 18) none — every rung is video-only or audio-only

Any selector branch that filters on audio codec, on ext=m4a, or that falls back to a combined best[ext=mp4] will miss all three times. A typical strict selector fails like this:

--format bestvideo[vcodec^=avc1]+bestaudio[acodec^=mp4a]   ← acodec is 'unknown', miss
        /bestvideo[ext=mp4]+bestaudio[ext=m4a]             ← audio ext is 'mp4', miss
        /best[ext=mp4]                                     ← no combined rung exists, miss

Note that the video half matches fine — the HLS rungs really are avc1. It is the audio filter and the combined-format fallback that have no candidate.

Fix

Append a codec-agnostic tail to the selector so there is always a final fallback:

--format bestvideo[vcodec^=avc1]+bestaudio[acodec^=mp4a]/bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/bestvideo+bestaudio/best

The tail only engages when every strict branch misses, so VOD behaviour is unchanged. Verify both directions rather than assuming — the point of the change is that it is inert on the common path:

# live stream: previously a hard error
yt-dlp --simulate --print "%(format_id)s %(resolution)s %(protocol)s" <LIVE_URL>
# 312+234 1920x1080 m3u8_native+m3u8_native

# VOD control: must be unchanged, strict branch still wins
yt-dlp --simulate --print "%(format_id)s %(vcodec)s %(acodec)s %(protocol)s" <VOD_URL>
# 133+140 avc1.4d400c mp4a.40.2 https+https

Caveat: the HLS Ladder Caps Quality

The live ladder tops out lower than the eventual VOD, and 60 fps rungs are avc1 only:

312  1920x1080  60fps  5553k  avc1.4D402A
311  1280x720   60fps  2922k  avc1.4D4020
231   854x480   30fps  1283k  avc1.4D401F
234  audio only               acodec unknown (AAC)

If the broadcast has 1440p or 4K rungs, they exist only in the DASH manifest the blocked clients would have returned — visionos never exposes them. If quality matters more than immediacy, wait for the VOD rather than recording the live HLS: once processing completes, the URL gets the full DASH ladder and a plain single-pass download.

If you do want the live capture, remember that a live download runs until the broadcast ends, and that yt-dlp starts at the live edge by default — add --live-from-start to get the whole broadcast, at the cost of a long-running process.

Why This Gets Misdiagnosed

Three separate red herrings point at the wrong page:

  1. Downloading visionos player API JSON looks like the client-pinning workaround at play, and the client list genuinely is degraded for live URLs — but that is a symptom, not the cause.
  2. The other clients returning nothing reads as blocking or rate-limiting.
  3. The failure arrives in the same shell session, on the same host, as the 403 family — so the instinct is to reach for PO tokens, .part cleanup, or a client pin.

The discriminator is one command. A 403 family failure fetches bytes and then fails; this one never fetches anything, and --list-formats succeeds while the download does not. If --list-formats prints a ladder but the download says the format is unavailable, the problem is your selector, not your access. Check live_status before anything else.