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

265 lines
8.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: yt-dlp YouTube JS Challenge Fix (Fedora)
domain: troubleshooting
category: general
tags:
- yt-dlp
- fedora
- youtube
- javascript
- deno
status: published
created: 2026-04-02
updated: 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:
```bash
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
```bash
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:
```bash
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:
```bash
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):**
```bash
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):**
```bash
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:**
```bash
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:
```bash
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:
```bash
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):**
```bash
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:**
```bash
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:
```bash
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:
```bash
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:**
```bash
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):**
```bash
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:**
```bash
yt-dlp --list-formats https://youtu.be/VIDEO_ID
```