majorwiki/05-troubleshooting/yt-dlp-fedora-js-challenge.md
majorlinux 2121d3ff1b yt-dlp: document -U trap and avoid duplicate pip installs
Add a Maintenance subsection covering why 'yt-dlp -U' fails on PyPI
builds and how to update via pip, plus how to detect/remove a duplicate
user+system install (the issue hit on majorhome 2026-06-16).
2026-06-16 19:12:06 -04:00

8.5 KiB
Raw Blame History

title domain category tags status created updated
yt-dlp YouTube JS Challenge Fix (Fedora) troubleshooting general
yt-dlp
fedora
youtube
javascript
deno
published 2026-04-02 2026-06-16T18:35

yt-dlp YouTube JS Challenge Fix (Fedora)

Problem

When running yt-dlp on Fedora, downloads may fail with the following warnings and errors:

WARNING: [youtube] No supported JavaScript runtime could be found.
WARNING: [youtube] [jsc:deno] Challenge solver lib script version 0.3.2 is not supported (supported version: 0.4.0)
WARNING: [youtube] 0qhgPKRzlvs: n challenge solving failed: Some formats may be missing.
ERROR: Did not get any data blocks
ERROR: fragment 1 not found, unable to continue

This causes subtitle downloads (and sometimes video formats) to fail silently, with the MP4 completing but subtitles being skipped.

Root Causes

  1. No JavaScript runtime installed — yt-dlp requires Deno or Node.js to solve YouTube's JS challenges
  2. Outdated yt-dlp — the bundled challenge solver script is behind the required version
  3. Remote challenge solver not enabled — the updated solver script must be explicitly fetched

Fix

1. Install Deno

Deno is not in the Fedora repos. Install via the official installer:

curl -fsSL https://deno.land/install.sh | sh
sudo mv ~/.deno/bin/deno /usr/local/bin/deno
deno --version

Alternatively, Node.js works and is available via sudo dnf install nodejs.

2. Update yt-dlp

sudo pip install -U yt-dlp --break-system-packages

If installed via standalone binary: yt-dlp -U

3. Enable Remote Challenge Solver

Add --remote-components ejs:github to the yt-dlp command. This fetches the latest JS challenge solver from GitHub at runtime:

yt-dlp -f 'bestvideo[vcodec^=avc]+bestaudio[ext=m4a]/bestvideo+bestaudio' \
  --merge-output-format mp4 \
  -o "/plex/plex/%(title)s.%(ext)s" \
  --write-auto-subs --embed-subs \
  --remote-components ejs:github \
  https://www.youtube.com/watch?v=VIDEO_ID

4. Persist the Config

Create a yt-dlp config file so --remote-components is applied automatically:

mkdir -p ~/.config/yt-dlp
echo '--remote-components ejs:github' > ~/.config/yt-dlp/config

Maintenance

YouTube pushes extractor changes frequently. Keep yt-dlp current.

Updating: the -U trap + avoid duplicate installs

yt-dlp -U does not work when yt-dlp was installed via pip/PyPI — the PyPI build deliberately disables the self-updater:

ERROR: You installed yt-dlp with pip or using the wheel from PyPi; Use that to update

Update through pip instead. Pick one install method and stick to it — running both a user install and a system install leaves two copies that drift out of sync (one updates, the other stays stale and shadows it depending on $PATH / sudo).

Recommended — single user install (no sudo):

pip3 install -U --user yt-dlp

This lives in ~/.local/bin/yt-dlp and is first on a normal user's $PATH. Update it the same way; never use sudo.

Alternative — system-wide (Fedora, PEP 668):

sudo pip install -U yt-dlp --break-system-packages

Only use --break-system-packages if you intentionally want a root-owned copy in /usr/local. Do not mix it with a --user install.

Check for and remove a duplicate install:

which -a yt-dlp            # more than one path = duplicate installs
sudo pip3 uninstall -y yt-dlp   # removes the /usr/local (system) copy + its wrapper

If installed via the standalone binary (not pip), yt-dlp -U is the correct updater.


Known Limitations

n-Challenge Failure: "found 0 n function possibilities"

Even with Deno installed, the remote solver downloaded, and yt-dlp up to date, some YouTube player versions can still fail n-challenge solving:

WARNING: [youtube] [jsc] Error solving n challenge request using "deno" provider:
Error running deno process (returncode: 1): error: Uncaught (in promise)
"found 0 n function possibilities".
WARNING: [youtube] n challenge solving failed: Some formats may be missing.
ERROR: [youtube] Requested format is not available.

This is a known upstream issue tied to specific YouTube player builds (e.g. e42f4bf8). It is not fixable locally — it requires a yt-dlp patch when YouTube rotates the player.

Workaround: Use a permissive format fallback instead of forcing AVC:

yt-dlp -f 'bestvideo+bestaudio/best' \
  --merge-output-format mp4 \
  -o "/plex/plex/%(title)s.%(ext)s" \
  --write-auto-subs --embed-subs \
  --remote-components ejs:github \
  https://www.youtube.com/watch?v=VIDEO_ID

This lets yt-dlp pick the best available format rather than failing on a missing AVC stream. To inspect what formats are actually available:

yt-dlp --list-formats --remote-components ejs:github \
  https://www.youtube.com/watch?v=VIDEO_ID

HTTP 429 Too Many Requests + Impersonation Warning

Downloads or subtitle fetches fail with:

WARNING: The extractor specified to use impersonation for this download,
but no impersonate target is available.
ERROR: Unable to download video subtitles for 'en-en-US': HTTP Error 429: Too Many Requests

Cause: yt-dlp needs curl_cffi to impersonate a real browser's TLS fingerprint. Without it, YouTube detects the non-browser client and rate-limits with 429s. Subtitle downloads are usually the first to fail (YouTube's timedtext endpoint has its own, stricter per-IP bucket).

Fix (pin curl_cffi to the supported range):

pip3 install --user -U "curl_cffi>=0.10,<0.15" "yt-dlp-ejs>=0.8"

⚠️ Do not run a bare pip install -U curl_cffi. As of yt-dlp 2026.03.17, the backend in yt_dlp/networking/_curlcffi.py hard-caps at 0.14.x:

ImportError: Only curl_cffi versions 0.5.10 and 0.10.x through 0.14.x are supported

Installing curl_cffi 0.15.0 silently disables impersonation — yt-dlp --list-impersonate-targets will show every source as (unavailable) even though import curl_cffi works fine. Always pin to <0.15 until yt-dlp widens the range.

Verify:

yt-dlp --list-impersonate-targets | head -5

Should show real entries (Chrome-133 Macos-15 curl_cffi), not the (unavailable) table.

If the 429 persists on subtitles only: the timedtext bucket is already hot from prior retries. Either wait 1560 min, skip subs for this download (--no-write-subs --no-write-auto-subs), or throttle with --sleep-subtitles 5 on retry. The video/audio path is not affected.

Companion gotcha — yt-dlp-ejs version drift: if yt-dlp -U reports yt-dlp is current but you still see:

WARNING: Challenge solver lib script version 0.3.2 is not supported ... supported version: 0.8.0

…then the EJS solver helper is stale. yt-dlp -U does not update it. Upgrade explicitly:

pip3 install --user -U yt-dlp-ejs

SABR-Only Streaming Warning

Some videos may show:

WARNING: [youtube] Some android_vr client https formats have been skipped as they
are missing a URL. YouTube may have enabled the SABR-only streaming experiment.

This is a YouTube-side experiment. yt-dlp falls back to other clients automatically — no action needed.

pip Version Check

pip show does not accept --break-system-packages. Run separately:

yt-dlp --version
pip show yt-dlp

Format Not Available: Strict AVC+M4A Selector

The format selector bestvideo[vcodec^=avc]+bestaudio[ext=m4a] will hard-fail if YouTube doesn't serve H.264 (AVC) video for a given video:

ERROR: [youtube] Requested format is not available. Use --list-formats for a list of available formats

This is separate from the n-challenge issue — the format simply doesn't exist for that video (common with newer uploads that are VP9/AV1-only).

Fix 1 — Relax the selector to mp4 container without enforcing codec:

yt-dlp -f 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio' \
  --merge-output-format mp4 \
  -o "/plex/plex/%(title)s.%(ext)s" \
  --write-auto-subs --embed-subs \
  https://youtu.be/VIDEO_ID

Fix 2 — Let yt-dlp pick best and re-encode to H.264 via ffmpeg (Plex-safe, slower):

yt-dlp -f 'bestvideo+bestaudio' \
  --merge-output-format mp4 \
  --recode-video mp4 \
  -o "/plex/plex/%(title)s.%(ext)s" \
  --write-auto-subs --embed-subs \
  https://youtu.be/VIDEO_ID

Use --recode-video mp4 when Plex direct play is required and the source stream may be VP9/AV1. Requires ffmpeg.

Inspect available formats first:

yt-dlp --list-formats https://youtu.be/VIDEO_ID