ssl.SSLError
PythonERRORCriticalConnection ErrorHIGH confidence

TLS/SSL certificate or handshake error

Production Risk

CRITICAL if bypassed; always fix the certificate rather than disabling verification.

What this means

Raised by the ssl module when a TLS/SSL error occurs — certificate verification failure, handshake error, or protocol mismatch. The most common cause is an untrusted or expired certificate.

Why it happens
  1. 1Server certificate is self-signed or from an untrusted CA
  2. 2Certificate has expired
  3. 3Hostname does not match the certificate CN/SAN
  4. 4TLS protocol version mismatch (e.g., server requires TLS 1.3, client only has 1.2)
How to reproduce

Connecting to a server with an expired or self-signed certificate.

trigger — this will error
trigger — this will error
import ssl
import urllib.request
urllib.request.urlopen('https://expired.badssl.com/')

expected output

ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired

Fix 1

Fix the server certificate

WHEN The certificate is expired or invalid

Fix the server certificate
# Check certificate details
openssl s_client -connect hostname:443 </dev/null 2>/dev/null | openssl x509 -noout -dates -subject -issuer
# Renew with Let's Encrypt:
certbot renew

Why this works

Certificates must be valid, not expired, and match the hostname. Let's Encrypt provides free 90-day certificates.

Fix 2

Use a custom CA bundle for internal services

WHEN Connecting to internal services with private CAs

Use a custom CA bundle for internal services
import ssl
import urllib.request

ctx = ssl.create_default_context(cafile='/path/to/internal-ca.pem')
urllib.request.urlopen('https://internal-service/', context=ctx)

Why this works

Providing the internal CA certificate allows verification without disabling SSL.

Code examples
Triggerpython
import urllib.request
urllib.request.urlopen("https://expired.badssl.com/")  # SSLCertVerificationError
Handle with try/exceptpython
import ssl, urllib.request
try:
    response = urllib.request.urlopen(url)
except ssl.SSLError as e:
    print(f"SSL error: {e}")
Avoid by using valid certificatespython
import ssl, urllib.request
ctx = ssl.create_default_context(cafile="/path/to/ca.pem")
response = urllib.request.urlopen(url, context=ctx)
What not to do

Disable SSL verification with verify=False or ssl.CERT_NONE

This makes MITM attacks trivial; attackers can intercept all traffic. Only use in isolated test environments, never production.

Content generated with AI assistance and reviewed for accuracy. Found an error? hello@errcodes.dev

← All Python errors