¿Fallan todas las llamadas al modelo tras la v0.22.6? Revisa primero la ruta de SSL_CERT_FILE


La red de tu empresa pasa por un proxy que abre el HTTPS y lo inspecciona. Eso lo resolviste hace meses: exportaste la raíz corporativa como bundle, export SSL_CERT_FILE=/etc/corp/ca-bundle.pem, y curl, pip y git se comportan bien. Luego actualizas Hermes, envías un mensaje y lo ves quedarse parado: certificate is not trusted en el log, tres reintentos y un “Provider temporarily unavailable” seco.

Tu red no está rota y la configuración del proxy no está mal. Desde la v0.22.6, el cliente de modelos resuelve la confianza de certificados por una ruta nueva, y el bundle indicado en SSL_CERT_FILE no viajó con ella.

Primero, confirma que es este problema

Los síntomas son bastante distintivos:

  • Fallan todos los provider, no solo uno, y cambiar de modelo no cambia nada.
  • La cadena de error es httpcore.ConnectError → httpx.ConnectError → openai.APIConnectionError: Connection error., con "[host] certificate is not trusted" en medio.
  • curl u openssl s_client llegan al mismo host sin problema, porque sí leen SSL_CERT_FILE.
  • Volver a la v0.22.5 en la misma máquina devuelve el funcionamiento normal.

Cuatro de cuatro significa que el asunto es la resolución de confianza de certificados, no la política de red.

Qué cambió en el código

En agent/ssl_verify.py, añadido en la v0.22.6, resolve_httpx_verify() responde a la pregunta de contra qué confianza debe verificar este cliente HTTP. El orden es: verificación desactivada de forma explícita → un bundle ssl_ca_cert explícito → el almacén de la plataforma. El problema está justo antes de ese último paso:

if os.environ.get("SSL_CERT_FILE") or os.environ.get("SSL_CERT_DIR"):
    return _shared_context(None)

_shared_context(None) significa “solo el almacén de la plataforma, sin CA bundle adicional”. Así que exportar SSL_CERT_FILE te empuja a una rama que lo ignora.

El propio docstring del módulo declara el contrato contrario: un bundle exportado en SSL_CERT_FILE se considera confiable además de el almacén de la plataforma, y solo la configuración explícita por provider (ssl_ca_cert, ssl_verify: false) sustituye esa confianza. platform_ssl_context(), en el mismo archivo, implementa exactamente esa unión. La rama de la variable de entorno y el resto del módulo no dicen lo mismo.

Por qué Linux parecía ir bien es la parte interesante: el backend OpenSSL de truststore llama a set_default_verify_paths(), y OpenSSL lee SSL_CERT_FILE por su cuenta, así que la confianza llegaba por accidente. macOS verifica mediante SecTrustSetAnchorCertificates y Windows mediante CryptoAPI; ninguno de los dos consulta el entorno de OpenSSL, de modo que ambos fallan en seco. Por eso la misma configuración corporativa rompe el Mac de un colega mientras tu máquina Linux sigue funcionando.

Tres formas de sortearlo, por orden de preferencia

1. Instala la raíz en el almacén de confianza del sistema operativo (lo que recomienda la documentación)

La documentación de seguridad es explícita: instala la raíz de un proxy TLS corporativo siguiendo el procedimiento de confianza del sistema operativo de tu organización. Cuando eso esté hecho, SSL_CERT_FILE deja de importar para las llamadas a los provider. En macOS, marca el certificado como siempre confiable en Acceso a Llaveros; en Windows, impórtalo en Entidades de certificación raíz de confianza con certlm.msc; en Debian/Ubuntu, deja el pem en /usr/local/share/ca-certificates/ y ejecuta update-ca-certificates. Es la opción más limpia, sobrevive a las actualizaciones y pide permisos de administrador.

2. Apunta el provider al bundle de forma explícita

Si no puedes tocar el almacén del sistema, define el CA bundle en la entrada del provider:

providers:
  deepseek:
    api: https://api.deepseek.com
    key_env: DEEPSEEK_API_KEY
    ssl_ca_cert: ${env:SSL_CERT_FILE}

ssl_ca_cert sustituye la confianza de plataforma para el chat, los metadatos de modelo y las sondas de catálogo de ese endpoint. Solo arregla este provider, que suele ser justo lo que quieres; una ruta equivocada registra un aviso y vuelve a la confianza de plataforma en lugar de fallar en silencio.

3. Desactiva la verificación (solo para desarrollo local)

ssl_verify: false hace que las llamadas funcionen al instante y te abre a cualquier man-in-the-middle del camino: detrás de un proxy corporativo que inspecciona, eso significa confiar en el proxy sin condiciones. La documentación lo califica de inseguro para redes no confiables y advierte de que no vale como arreglo permanente de una raíz que falta. Úsalo para diagnosticar, no para producción.

Cómo se arregla esto

El PR del arreglo (#148253) cambia esa rama para que devuelva platform_ssl_context() en lugar de _shared_context(None), restaura la unión documentada y explica por qué Linux parecía funcionar. A la altura de la release v0.22.7 sigue abierto, así que la build estable que instales hoy se comporta como antes. Si lo estás esperando, la comprobación es sencilla: una release después, mira si esa rama de agent/ssl_verify.py devuelve platform_ssl_context().

Una costumbre que conviene mantener

Los problemas de certificados se disfrazan de “la red está caída” o “el provider tiene una incidencia”, porque el síntoma de superficie es un error de conexión. La próxima vez que fallen todos los provider a la vez, haz primero una prueba por capas: curl --cacert "$SSL_CERT_FILE" https://<host>. Si eso funciona, el fallo está en la resolución de confianza de la aplicación, no en la red, y te acabas de ahorrar media hora de capturas de paquetes.

Para seguir leyendo: modelos locales y certificados autofirmados, cifrar secretos en local, qué más hay en la ventana de la v0.22.7, instalar y actualizar.