preloader
post-thumb

Last Update: August 19, 2026


BYauthor-thumberic

|Loading...

Keywords

yt-dlp gave us the least helpful error message it has: HTTP Error 410: Gone. The standard fix is yt-dlp -U. We ran it. Already on the latest stable release. The video was not, in fact, gone — but proving that took three separate bugs stacked on top of each other, and the last one had nothing to do with yt-dlp at all.

We were debugging a small local downloader tool — a browser extension that hands a page URL and cookies to yt-dlp on the user's own machine, optionally routed through a local HTTP proxy for geo-unblocking. One specific site started failing every time, on a URL that worked fine a week earlier.

The first clue was buried in -v

The plain error told us nothing. Adding -v for verbose output surfaced a line that's easy to scroll past:

WARNING: [SiteExtractor] The extractor is attempting impersonation, but no
impersonate target is available. If you encounter errors, then see
https://github.com/yt-dlp/yt-dlp#impersonation for information on
installing the required dependencies

That's the real signal. A growing number of sites fingerprint the TLS handshake and HTTP/2 frame ordering of incoming requests to tell a real browser apart from a scripted client — even when every header looks identical. yt-dlp can spoof a browser's TLS fingerprint via an optional dependency, curl_cffi, which wraps libcurl with impersonation profiles for Chrome, Safari, Firefox and so on. Without it, some extractors still try the request, get rejected at the TLS layer, and the site's edge returns a generic 410 instead of anything that explains what actually happened. The video was fine. yt-dlp just didn't look enough like a browser to be allowed to ask.

The version window nobody tells you about

The fix looked trivial:

sh
python3 -m pip install curl_cffi

It installed cleanly — curl_cffi-0.16.0, the latest release. yt-dlp still refused to use it, and --list-impersonate-targets came back with every target marked (unavailable). The debug line was the giveaway:

[debug] Optional libraries: ... curl_cffi-0.16.0 (unsupported) ...

yt-dlp doesn't just check that curl_cffi is importable — it pins a narrow set of versions it actually trusts, because curl_cffi's impersonation API has changed shape across releases. Digging through the installed yt-dlp build (it ships as a zip-app; python -m zipfile opens it like any other zip) turned up the exact check:

python
curl_cffi_version = tuple(map(int, re.split(r'[^\d]+', curl_cffi.__version__)[:3]))

if curl_cffi_version != (0, 5, 10) and not (0, 10) <= curl_cffi_version < (0, 16):
    curl_cffi._yt_dlp__version = f'{curl_cffi.__version__} (unsupported)'
    raise ImportError('Only curl_cffi versions 0.5.10 and 0.10.x through 0.15.x are supported')

Latest-and-greatest was one minor version too new. The fix was to pin below the ceiling:

sh
python3 -m pip install "curl_cffi>=0.10,<0.16"

Lesson: when a library depends on another native-extension package for an optional feature, don't assume "latest" is "supported" — check for a version gate specifically on the feature you're trying to turn on, not just on whether the import succeeds.

Collateral damage from --force-reinstall

The first install attempt used --force-reinstall to make sure the correct version actually took. It did — but it also silently upgraded packages that were already correctly pinned for something else entirely: certifi, cffi, markdown-it-py, and pygments all jumped to newer versions than another tool in the same shared Python environment required. pip said so plainly, right at the end of the install:

ERROR: pip's dependency resolver does not currently take into account all
the packages that are installed. This behaviour is the source of the
following dependency conflicts.
some-other-tool 2.15.0 requires certifi==2026.4.22, but you have
certifi 2026.7.22 which is incompatible.
some-other-tool 2.15.0 requires cffi==2.0.0, but you have cffi 2.1.1 ...

None of those four packages actually needed upgrading — curl_cffi's own requirement (cffi>=2.0.0, certifi>=2024.2.2) was already satisfied by the versions in place. --force-reinstall doesn't just reinstall the package you named; it re-resolves and reinstalls its entire dependency subtree, and when it does that it grabs the latest version satisfying each constraint rather than leaving an already-satisfied pin alone. In a shared base environment with several unrelated tools installed, that's a quiet way to break something you weren't even looking at.

The fix was to pin those four straight back:

sh
python3 -m pip install "certifi==2026.4.22" "cffi==2.0.0" \
  "markdown-it-py==4.0.0" "pygments==2.20.0"

Lesson: avoid --force-reinstall in a shared or base Python environment. If you need a specific version of one package, ask for exactly that (pip install "pkg==x.y.z") rather than forcing a full dependency re-resolve you don't control.

A new error, and a wrong theory

With the right curl_cffi version in place, the impersonation warning was gone — replaced by a new one, appearing on every single attempt:

SSLError: Failed to perform, curl: (35) TLS connect error:
error:00000000:invalid library (0):OPENSSL_internal:invalid library (0)

This looks exactly like a known class of curl_cffi bug: its bundled BoringSSL build can clash with another OpenSSL-family library already loaded in the same Python process — commonly cryptography, pulled in transitively by something unrelated like browser-cookie decryption support. The natural fix is isolation: a clean virtual environment with only yt-dlp and curl_cffi, nothing that could drag in a conflicting crypto library.

We built that environment. Same error, 100% reproducible, in complete isolation — no cryptography, no secretstorage, nothing else installed at all.

That result was more useful than a fix would have been. A clean-environment reproduction rules out an entire category of theories in one shot. It wasn't a Python packaging conflict. It had to be something outside Python's import graph.

Stripping back to the dumbest possible test

The next step was to remove every layer of abstraction and test the actual thing that was failing: a TLS handshake over the proxy. Not curl_cffi. Not even yt-dlp. Plain, unimpersonated requests:

python
import requests
r = requests.get("https://www.example.com",
                  proxies={"https": "http://127.0.0.1:8082"})

Same SSL error. Against a domain with nothing to do with the original site. That was the moment the whole investigation flipped: this was never a curl_cffi bug, or even a Python bug. Something about the proxy itself was breaking HTTPS.

Raw curl confirmed it in the plainest terms available:

* CONNECT tunnel established, response 200
> Client hello (1)
* OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to www.example.com:443

The proxy's CONNECT tunnel came up cleanly — a 200 Connection Established — and then the TLS handshake died immediately afterward, for every client, on every site. Nothing about curl_cffi's TLS fingerprint mattered; the tunnel itself wasn't relaying bytes correctly once real TLS traffic started flowing through it.

The proxy client had several selectable exit gateways by region. Switching from one region to another and rerunning the exact same curl command fixed it instantly — clean 200, valid response, every time. The original gateway node was in a broken state for HTTPS relaying specifically; nothing in our code, yt-dlp, or curl_cffi was ever going to fix that from the client side.

What actually shipped

Three real problems, three real fixes, none of them where the first error message pointed:

Symptom
Looked like
Actually was
HTTP 410: Gone
Stale yt-dlp / dead video
Site requires TLS impersonation
curl_cffi "(unsupported)"
Broken install
Version above yt-dlp's pinned ceiling
Unrelated tool broke after install
Unrelated bug
--force-reinstall re-resolved shared deps
SSL "invalid library" error
curl_cffi vs OpenSSL clash
Proxy gateway failing the TLS relay

The lasting fix was to isolate curl_cffi-enabled yt-dlp into its own virtual environment — so a future dependency bump anywhere else on the machine can never touch it again — and to symlink the PATH-resolved yt-dlp at that environment's build, so every caller picks it up without extra configuration.

The general shape of this kind of bug

None of the individual fixes here are exotic. What made this one take longer than it should have was trusting each error message's own framing for too long:

  • "410 Gone" told us the video was gone. It wasn't — it was an anti-bot response wearing the shape of a different error.
  • "unsupported" told us the install was broken. It wasn't broken, it was one version too new for a pin we didn't know existed.
  • "invalid library" pointed straight at curl_cffi and OpenSSL. It was neither — it was a proxy gateway with a bad TLS relay.

The pattern that actually broke each false lead was the same one every time: reproduce with the fewest possible moving parts. A clean virtual environment ruled out the packaging theory. Plain requests with no impersonation ruled out curl_cffi entirely. Raw curl against an unrelated domain ruled out the target site. Each step removed one whole category of suspects instead of guessing at the next fix — and the actual root cause, when it finally showed up, wasn't in any of the code we'd been staring at.

Comments (0)

Leave a Comment
Your email won't be published. We'll only use it to notify you of replies to your comment.
Loading comments...
Previous Article
post-thumb

Oct 03, 2021

Setting up Ingress for a Web Service in a Kubernetes Cluster with NGINX Ingress Controller

A simple tutorial that helps configure ingress for a web service inside a kubernetes cluster using NGINX Ingress Controller

Next Article
post-thumb

Aug 19, 2026

Letting an AI Read Email Without Letting It Read Everyone's

Giving an AI assistant read-and-draft access to a Microsoft 365 mailbox is easy. Making sure it can touch only the right mailbox — enforced by Microsoft, not by your own code — is where the real work is.

agico

We transform visions into reality. We specializes in crafting digital experiences that captivate, engage, and innovate. With a fusion of creativity and expertise, we bring your ideas to life, one pixel at a time. Let's build the future together.

Copyright ©  2026  TYO Lab · v0.0.18