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,mwebandwebwere all contacted and all came back with zero formats. - Every returned format is
m3u8. There is not a singlehttps(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:
Downloading visionos player API JSONlooks 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.- The other clients returning nothing reads as blocking or rate-limiting.
- 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,
.partcleanup, 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.