Skip to content
ecdsa.com
Python ssl module (OpenSSL)Python cryptography

Error message

[SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocol

Also printed as

  • ssl.SSLEOFError: EOF occurred in violation of protocol

With OpenSSL 3, Python shows the reason code "[SSL: UNEXPECTED_EOF_WHILE_READING]"; with OpenSSL 1.1.1 the text is just "EOF occurred in violation of protocol". A source location such as "(_ssl.c:1082)" follows.

Python ssl module (OpenSSL) — ssl.SSLEOFError, SSL_R_UNEXPECTED_EOF_WHILE_READING

What it means

The other side closed the TCP connection while Python was still in the TLS handshake (or in the middle of reading data), without sending a TLS alert or close_notify. OpenSSL treats that silent hang-up as a protocol violation. Python cannot know why the peer left; the reason is on the server, a proxy or a firewall in between.

Why it happens

How to fix it

  1. 1.

    Reproduce the handshake with openssl s_client

    If s_client also gets disconnected, vary SNI and the TLS version to see what the server objects to; if s_client works, compare its settings with your Python client (proxy, SNI, versions).

    bash
    # Does a plain OpenSSL handshake work from the same machine?
    openssl s_client -connect api.example.com:443 -servername api.example.com -brief </dev/null
    # Without SNI, and pinned to TLS 1.2, to see what the server objects to
    openssl s_client -connect api.example.com:443 -noservername -brief </dev/null
    openssl s_client -connect api.example.com:443 -servername api.example.com -tls1_2 -brief </dev/null
  2. 2.

    Check proxy environment variables

    requests, urllib and pip honour HTTPS_PROXY and NO_PROXY. Fix the proxy URL scheme and exclude internal hosts that must be reached directly.

    bash
    env | grep -i -E '^(https?|no|all)_proxy='
    # Proxy URLs should normally be http:// (CONNECT tunnel), and internal hosts listed in NO_PROXY
    export HTTPS_PROXY=http://proxy.example.net:3128
    export NO_PROXY=localhost,127.0.0.1,.internal.example.com
  3. 3.

    Retry when the cause is transient

    For rate limiting and flaky networks, retries with backoff turn sporadic resets into slower successes; they do not fix a server that always refuses the handshake.

    python
    import requests
    from requests.adapters import HTTPAdapter
    from urllib3.util.retry import Retry
    
    session = requests.Session()
    session.mount("https://", HTTPAdapter(max_retries=Retry(total=3, backoff_factor=1)))
    session.get("https://api.example.com/")

Related errors

Browse the full signature error database