Geonode logo
Secure connection errors

How to Fix EPROTO (SSL Wrong Version Number)

Find out whether a proxy URL or a port caused it, then fix it in Python, Node.js or cURL.

Updated

TL;DR

EPROTO with wrong version number means your client opened a TLS handshake and the proxy or server answered in plain text, so the handshake stopped at the first reply. The fastest fix, when a proxy is set, is to start its URL with http://, both in code and in HTTPS_PROXY.

What EPROTO looks like in each client

Seen on the versions named. The wording changes with the OpenSSL version, and macOS's built-in curl reports a protocol version alert.

WhereWhat you see
Node.js 20.17 and 20.19 https module (OpenSSL 3.0)Error: write EPROTO 80E1A9ED01000000:error:0A00010B:SSL routines:ssl3_get_record:wrong version number:../deps/openssl/openssl/ssl/record/ssl3_record.c:355:
Node.js 24.22 fetch() (OpenSSL 3.5.8), in err.cause (code ERR_SSL_WRONG_VERSION_NUMBER; the thrown error is TypeError: fetch failed)Error: 80E1A9ED01000000:error:0A00010B:SSL routines:tls_validate_record_header:wrong version number:../deps/openssl/openssl/ssl/record/methods/tlsany_meth.c:78:
Node.js 24.11 https module (OpenSSL 3.5.4)Error: write EPROTO 80E1A9ED01000000:error:0A0000C6:SSL routines:tls_get_more_records:packet length too long:../deps/openssl/openssl/ssl/record/methods/tls_common.c:663:
npm 11 with an https:// proxynpm error code EPROTO
Python 3.12 urllib (OpenSSL 3.0)urllib.error.URLError: <urlopen error [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1000)>
pip 24 with an https:// proxy (its bundled urllib3 1.26, the library under requests)ProxyError('Your proxy appears to only use HTTP and not HTTPS, try changing your proxy URL to be HTTP. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#https-proxy-error-http-proxy', SSLError(SSLError(1, '[SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1000)')))
curl 8.7 on macOS (system build with LibreSSL 3.3.6)curl: (35) LibreSSL/3.3.6: error:1404B42E:SSL routines:ST_CONNECT:tlsv1 alert protocol version

Why this happens

Your client spoke TLS, but the other end replied in plain text.

A common case is a proxy URL that starts with https:// for a proxy port that speaks plain HTTP. OpenSSL reads the reply's first five bytes as a TLS record header and finds no valid version there.

Diagnose your EPROTO first

Each check points to one cause: the proxy URL, a plain-HTTP port or a TLS version mismatch.

  • Open the same address with http:// in a browser; if the page loads, that port serves plain HTTP, not TLS.

  • Run env | grep -i _proxy; an HTTPS_PROXY or ALL_PROXY value starting with https:// makes your tools open TLS with the proxy itself.

  • With no proxy variables set, run python -c 'import urllib.request; print(urllib.request.getproxies())'; an https:// value comes from system proxy settings.

  • Run curl -v through the proxy; a TLS Client hello before any CONNECT line means curl is handshaking with the proxy, not the site.

  • Read the reason after the code; unsupported protocol instead means the server does speak TLS, but in a version your client refuses.

Solutions ranked by effectiveness

Fix the proxy URL first; the other two cover plain-HTTP ports and mail servers.

  1. Most common fix

    Start the proxy URL with http://

    Applies when only proxied requests fail and the provider documents an http:// address. Use http:// in code, HTTPS_PROXY and npm's https-proxy; the CONNECT tunnel still carries end-to-end TLS.

    # pip install requests
    import requests
    
    proxy = "http://USERNAME:PASSWORD@proxy.geonode.io:9000"
    proxies = {"http": proxy, "https": proxy}
    r = requests.get("https://example.com/", proxies=proxies, timeout=30)
    print(r.status_code)
  2. Check next

    Point https:// at a TLS port

    Applies when the error appears without a proxy, because the port you call answers in plain HTTP, such as a local dev server started without a certificate.

    1. Use http:// for a local server that has no certificate.

    2. Use https:// only on the port your API documents; with no port, it means 443.

    3. For HTTPS on your own server, enable TLS there and call its TLS port.

  3. For developers

    Match implicit TLS to the mail port

    Applies to SMTP clients. Port 587 opens in plain text and upgrades with STARTTLS, so a client that starts TLS at once reads the server's 220 greeting as a bad record.

    1. In Nodemailer, set secure: true only for port 465; leave it false for 587.

    2. In Python, use smtplib.SMTP_SSL on 465, and SMTP plus starttls() on 587.

Stop the EPROTO from coming back

Habits that keep schemes, ports and TLS modes paired correctly as configs and runtimes change.

  1. Validate proxy URLs at startup and reject https:// unless your provider documents a TLS proxy port.

  2. In Node.js, key monitoring on the EPROTO code and the ERR_SSL_ prefix, not message text, which changes with OpenSSL.

  3. Write in your setup docs that HTTPS_PROXY names the proxy for https:// sites, not one reached over TLS.

  4. Store mail settings as one URL, such as smtps://host:465 in Nodemailer, so port and TLS mode cannot drift apart.

One proxy URL for both schemesGeonode's HTTP ports carry https:// sites too, so HTTPS_PROXY takes the same http:// URL.
Try residential proxies

Related errors

Learn more

FAQ

EPROTO is the POSIX error code for a protocol error. Node.js adds write because it reports the TLS failure to a socket write waiting on the handshake; the OpenSSL text after it, usually wrong version number, gives the reason.

Run npm config list, which masks the password; if https-proxy or proxy starts with https:// for a plain HTTP proxy, set it again with http://. npm also reads HTTPS_PROXY, so check that variable too.

urllib3, the library under requests and pip, adds that hint when a proxy URL starting with https:// fails with wrong version number. Rewrite that URL, or HTTPS_PROXY, with http:// and run the request again.

Node.js 24.11 (OpenSSL 3.5.4) printed packet length too long for the same plain HTTP reply, because that OpenSSL checks a record's length before its version. Node.js 24.22 (OpenSSL 3.5.8) says wrong version number; the fix is the same.

No. The client gave up on the first reply, before any certificate arrived, so a new CA bundle changes nothing. Certificate errors name the certificate, for example self-signed certificate.

For https:// sites, Python's built-in urllib ignores the proxy URL's scheme: it sends CONNECT in plain text and starts TLS inside the tunnel. requests honors https:// and starts TLS with the proxy itself, which a plain HTTP proxy rejects.

Endpoints with matching ports

Change the protocol in Geonode's Endpoint Generator, and each endpoint's port updates to match.