YouTube media 403 on Windows despite deno + curl_cffi present: only player_client=mweb works, but mweb alone kills subtitles

Author: realsalestalkCreated Aug 18, 2026Updated Aug 27, 2026

Summary

On Windows with a fully-provisioned environment (deno present, curl_cffi impersonation targets available), every YouTube media download returns HTTP Error 403: Forbidden while captions download fine in the same run. The only player_client that returns usable media URLs is mweb.

This is not #93 (brew yt-dlp with zero impersonation targets) or #67 (missing deno/curl_cffi) — both of those root causes are absent here, and --impersonate does not help. Filing separately since the remedy is different.

Environment

  • Windows 11, Python 3.14.5
  • yt-dlp [email protected] (stable 2026.07.04 behaves identically)
  • ffmpeg N-125875-g5d4d3bdc61-20260731
  • yt-dlp --list-impersonate-targets → Chrome-133/136, Safari-17.2/18.0/18.4/26.0, etc. (curl_cffi bundled in the standalone exe)
  • deno installed and on PATH

What fails

yt-dlp -F lists formats fine, so the failure is specific to fetching media, not to extraction:

ERROR: unable to download video data: HTTP Error 403: Forbidden

Reproduces on any video, including a 19-second unrelated one, so it is not video-specific or rate limiting.

Tested per client on -f "worst[height<=360]/worst":

player_client media download
default 403
web 403
tv 403
web_safari 403
ios 403
mweb OK

--impersonate chrome and --impersonate safari also 403 with the default client.

The part that makes the obvious fix wrong

Setting player_client=mweb globally fixes media but silently breaks subtitles — mweb requires a PO token for them:

WARNING: [youtube] There are missing subtitles languages because a PO token was
not provided. Subtitles for these languages are missing: en, ...

So /watch then returns 100 frames and Transcript: none available, which reads like a captions bug rather than a side effect.

Listing both clients does not resolve it either — it just flips which half breaks:

extractor-args subtitles media
player_client=mweb missing OK
player_client=default,mweb OK 403
player_client=default OK 403

Fix that worked

Because download.py already makes two separate yt-dlp calls, the two passes can use different clients. In fetch_captions():

python
cmd = [
    "yt-dlp",
    "--extractor-args", "youtube:player_client=default",
    "--skip-download",
    ...

and in the media download:

python
cmd = [
    "yt-dlp",
    "--extractor-args", "youtube:player_client=mweb",
    "-N", "8",
    ...

With that split, captions and frames both work on the same run. Happy to open a PR if the approach looks right — though a maintainer may well know a cleaner client than mweb to pin the media pass to.

Aside

--cookies-from-browser chrome is not a usable fallback here: it fails with Could not copy Chrome cookie database whenever Chrome is running, which for most people is always.

Source: bradautomates/claude-video