Mensaje de error
<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
También se muestra como
- urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
El texto que va entre los corchetes angulares es el error de ssl subyacente y varía («self-signed certificate in certificate chain», «certificate has expired», …). Python lo cierra con una ubicación en el código fuente y «>», por ejemplo, «(_ssl.c:1082)>».
Python urllib.request — urllib.error.URLError wrapping ssl.SSLCertVerificationError
Qué significa
urllib.request.urlopen() envuelve cualquier problema de conexión en URLError, así que el fallo de TLS aparece dentro de «<urlopen error …>». Cuando esto ocurre con todos los sitios públicos, incluido www.python.org, el proceso de Python no tiene ningún certificado de CA utilizable: el caso clásico es el instalador de python.org en macOS, cuyo OpenSSL incluido no lee el llavero hasta que se ejecuta «Install Certificates.command».
Por qué ocurre
Python de python.org en macOS sin sus certificados
frecuenteEl instalador incluye OpenSSL con un almacén de confianza vacío y un script auxiliar, «Install Certificates.command», que instala certifi y lo enlaza. Si no se ejecuta (o si más tarde se instala una nueva versión menor), urlopen no puede verificar nada.
Una imagen mínima o una compilación personalizada de Python sin bundle de CA
ocasionalUn Python compilado desde el código fuente o copiado en una imagen scratch o distroless busca la ruta del bundle predeterminado de OpenSSL, que allí no existe.
Un proxy que inspecciona TLS
ocasionalSi solo fallan algunos sitios, o los fallos empezaron en una red de empresa, la causa probable es la raíz de un proxy en la que este Python no confía.
SSL_CERT_FILE o SSL_CERT_DIR apunta al lugar equivocado
raraEstas variables sustituyen las ubicaciones predeterminadas para todo el proceso; un archivo vacío o que no tiene nada que ver rompe todas las verificaciones.
Cómo solucionarlo
- 1.
macOS: ejecutar Install Certificates.command
Ejecútalo desde Finder o desde una terminal para cada versión de Python de python.org y, después, confirma que existe la ruta del archivo de CA predeterminado.
bash # macOS, python.org installer (one script per installed version) "/Applications/Python 3.14/Install Certificates.command" python3 -c "import ssl; print(ssl.get_default_verify_paths().openssl_cafile)" - 2.
Pasar un contexto creado a partir de certifi
En los scripts que deben funcionar en cualquier sitio, crea el contexto SSL explícitamente a partir de certifi.where(); mantén certifi actualizado para que sus raíces estén al día.
python # Explicit, portable: use certifi's Mozilla bundle for this request # pip install certifi import certifi, ssl, urllib.request ctx = ssl.create_default_context(cafile=certifi.where()) urllib.request.urlopen("https://www.python.org/", context=ctx) - 3.
O usar el almacén de confianza del sistema operativo
truststore.inject_into_ssl() hace que urllib, http.client y las bibliotecas construidas sobre ssl verifiquen a través del sistema operativo, incluidas las raíces corporativas que despliega el departamento de TI.
python # Or verify with the operating system's store (macOS Keychain, Windows, Linux bundle) # pip install truststore==0.10.5 import truststore truststore.inject_into_ssl() - 4.
Comprobar si el entorno sustituye las rutas predeterminadas
Elimina las variables SSL_CERT_FILE y SSL_CERT_DIR si apuntan a archivos que no contienen las raíces necesarias.
bash env | grep -E '^SSL_CERT_(FILE|DIR)=' python3 -c "import ssl; print(ssl.get_default_verify_paths())"