# Error de conexión de OpenAI API: corrige APIConnectionError probando la ruta

> Ante APIConnectionError, conserva la causa original y comprueba la conexión desde el mismo proceso. Un fallo de DNS, de certificados o de proxy requiere una corrección distinta; después verifica la petición que fallaba.

- URL: https://blog.laozhang.ai/es/posts/openai-api-error-connection-error
- Published: 2026-05-06
- Updated: 2026-10-07
- Author: LaoZhang AI Team (https://blog.laozhang.ai/es/about)
- Topic: Guías de API
- Tags: OpenAI API, APIConnectionError, error de conexión, SDK, Python, Node.js

---
`openai.APIConnectionError: Connection error.` indica que el SDK no pudo completar la comunicación con el endpoint configurado. **Empieza por capturar la causa original en el mismo proceso que falla**, con un tiempo de espera explícito y sin reintentos automáticos durante esa comprobación. Después distingue un fallo de DNS, de TLS, del proxy o de lectura de la respuesta; cada uno exige una acción diferente.

Si ya tienes un código HTTP, lee esa respuesta antes de investigar la conexión. Un `401`, un `429` y un `503` orientan hacia problemas distintos. Y si la respuesta se cortó después de enviar la petición —o después de recibir parte de un stream—, no puedes concluir que OpenAI no la procesó porque te falte el identificador. La [referencia oficial de errores](https://developers.openai.com/api/docs/guides/error-codes) separa los problemas de conexión de los errores devueltos por la API.

## Decisión rápida

| Qué has recibido | Por dónde empezar | Acción útil |
| --- | --- | --- |
| `APIConnectionError`, sin estado HTTP | Causa encadenada y configuración de red del proceso | Identifica DNS, certificado, conexión rechazada, proxy o desconexión antes de cambiar parámetros de la API |
| Excepción de timeout | Fase que agotó el plazo y límites de la aplicación | Distingue conexión, escritura y lectura; comprueba también el plazo del proxy o de la función |
| `401` | Autenticación del servicio que respondió | Comprueba el host y la configuración de acceso; aumentar el timeout no corrige ese rechazo |
| `429` | Cuerpo del error y cabeceras | Sigue la guía de [causas del error 429 y reintentos](https://blog.laozhang.ai/es/posts/openai-api-rate-limit); si indica `insufficient_quota`, revisa [cuota y topes de gasto](https://blog.laozhang.ai/es/posts/openai-api-quota-exceeded-error) |
| `500` o `503` | Servicio que emitió la respuesta y posible fallo temporal | Conserva estado, identificador y cabeceras útiles; consulta el estado de ese proveedor y aplica reintentos limitados cuando corresponda |
| Texto `upstream connect error` dentro de una respuesta HTTP | Proxy o pasarela que devuelve ese texto | Investiga su conexión con el siguiente servicio, sin atribuir automáticamente el fallo a OpenAI |

Un `503` sigue siendo una respuesta HTTP, aunque describa que un intermediario no consiguió conectar con su destino. Tener cabeceras no demuestra que las haya generado OpenAI: una pasarela puede emitir su propio error.

## Lee primero la evidencia de respuesta

Antes de modificar nada, anota el host de la URL base, el endpoint, la versión del SDK y el entorno donde falla: proceso local, contenedor, worker o función desplegada. Comprueba el valor **que utiliza ese proceso**, no solo el fichero de configuración que esperabas cargar. No copies claves ni URLs con credenciales a los registros.

En Python, `APITimeoutError` es una subclase de `APIConnectionError`; debes capturarla primero si necesitas distinguir ambas. `APIStatusError` expone `status_code`, `response` y `request_id`. Cuando falla el transporte, `__cause__` ayuda a recuperar la excepción subyacente. La [referencia actual del SDK de Python](https://developers.openai.com/api/reference/python) documenta estos campos y clases.

Este envoltorio se integra alrededor de **la llamada existente**, para conservar su cliente, endpoint, modelo y configuración. No crea otro cliente ni realiza una petición al importarlo:

~~~python
import openai


def describir_causas(error):
    causas = []
    actual = error.__cause__
    while actual is not None and len(causas) < 6:
        causas.append(type(actual).__name__)
        actual = actual.__cause__
    return causas


def observar(llamada):
    try:
        resultado = llamada()
        print({
            "resultado": "respuesta recibida",
            "request_id": getattr(resultado, "_request_id", None),
        })
        return resultado
    except openai.APITimeoutError as error:
        print({"fallo": "timeout", "causas": describir_causas(error)})
        raise
    except openai.APIConnectionError as error:
        print({"fallo": "conexion", "causas": describir_causas(error)})
        raise
    except openai.APIStatusError as error:
        print({
            "fallo": "respuesta HTTP",
            "status": error.status_code,
            "request_id": error.request_id,
        })
        raise
~~~

Por ejemplo, sustituye el contorno de una llamada síncrona a `client.responses.create(...)` por `observar(lambda: client.responses.create(...))`, conservando sus argumentos. Es una integración ilustrativa: los puntos suspensivos representan tu operación existente. Para un cliente asíncrono, aplica el mismo orden de captura alrededor del `await`; no utilices este envoltorio síncrono.

Los nombres de las excepciones suelen bastar para empezar sin exponer datos. Si necesitas su mensaje completo, examínalo en un registro protegido y elimina credenciales, parámetros privados y contenido de clientes antes de compartirlo. No vuelques por defecto todas las cabeceras ni el cuerpo.

### Controles SDK que conservan evidencia

![Controles del SDK para distinguir timeout, conexión y respuesta HTTP sin ocultar la causa](https://blog.laozhang.ai/posts/es/openai-api-error-connection-error/img/explainer-2.webp)

Los SDK actuales de Python y JavaScript reintentan por defecto dos veces determinados fallos aptos para reintento. Durante una comprobación aislada, usa `max_retries=0` en Python o `maxRetries: 0` en JavaScript: así puedes relacionar el resultado con un único intento del SDK, siempre que tampoco reintente la cola o tu aplicación. Restaura después una política limitada acorde con el trabajo. Las referencias de [Python](https://developers.openai.com/api/reference/python) y [JavaScript](https://developers.openai.com/api/reference/typescript) describen estas opciones.

El timeout predeterminado documentado es de diez minutos. **Python expresa el valor numérico en segundos; JavaScript, en milisegundos.** Para una comprobación puedes aplicar `client.with_options(max_retries=0, timeout=30.0)` en Python o `client.withOptions({ maxRetries: 0, timeout: 30_000 })` en JavaScript. Treinta segundos es un ejemplo para una operación pequeña, no una duración garantizada ni una recomendación para cualquier generación.

En Node.js, conserva también `error.cause` cuando exista. En el SDK actual, captura `OpenAI.APIConnectionTimeoutError` antes de `OpenAI.APIConnectionError`, y después `OpenAI.APIError` para leer `status` y `requestID`. Si la aplicación cancela con un `AbortSignal`, identifica esa cancelación y su plazo: aumentar el timeout del SDK no elimina un aborto que decide tu código. Consulta los [errores y controles de tiempo del SDK de JavaScript](https://developers.openai.com/api/reference/typescript).

## Prueba la misma ruta sin confundir las comprobaciones

![Las sondas de DNS, TLS y curl tienen un alcance distinto de la llamada instrumentada del proceso](https://blog.laozhang.ai/posts/es/openai-api-error-connection-error/img/explainer-1.webp)

Haz las pruebas desde el contenedor o la función que falla, con su DNS, certificados y salida de red. Que funcione tu portátil solo demuestra que tu portátil puede conectar. Incluso en la misma máquina, una terminal y un proceso pueden utilizar proxies o almacenes de certificados diferentes.

La comprobación más representativa es la llamada instrumentada anterior: mantiene el transporte del proceso. Para aislar una fase, añade una prueba más pequeña. **Una prueba directa de TCP/TLS no atraviesa necesariamente el proxy del SDK**, y `curl` tampoco reproduce automáticamente el transporte de Python o Node. Utiliza esas diferencias para comparar configuraciones, sin presentar una sonda independiente como prueba definitiva de la aplicación.

### DNS, TCP y certificado desde Python

Esta función se ejecuta solo cuando la llamas. Úsala dentro del proceso afectado para probar una conexión HTTPS **directa** al host configurado. No lleva una clave API, no envía una petición de generación y mantiene la validación del certificado:

~~~python
import socket
import ssl
from urllib.parse import urlsplit


def comprobar_conexion_directa(base_url, contexto_tls=None):
    destino = urlsplit(str(base_url))
    if destino.scheme != "https" or not destino.hostname:
        raise ValueError("Se necesita una URL HTTPS con host")
    host = destino.hostname
    puerto = destino.port or 443
    fase = "DNS"
    try:
        socket.getaddrinfo(host, puerto, type=socket.SOCK_STREAM)
        fase = "TCP"
        with socket.create_connection((host, puerto), timeout=5) as conexion:
            fase = "TLS"
            contexto = contexto_tls or ssl.create_default_context()
            with contexto.wrap_socket(conexion, server_hostname=host):
                print({"host": host, "resultado": "DNS, TCP y TLS correctos"})
    except (OSError, ssl.SSLError) as error:
        print({"host": host, "fase": fase, "causa": type(error).__name__})
        raise
~~~

Puedes pasar `client.base_url` de tu cliente existente. Si tu aplicación utiliza un contexto TLS corporativo autorizado, pasa ese mismo contexto; el contexto predeterminado puede usar otro conjunto de autoridades de confianza. El timeout de cinco segundos limita las operaciones de socket del ejemplo, pero no garantiza un plazo total de cinco segundos para la resolución DNS.

Si necesitas un proxy para salir, la imposibilidad de conectar directamente puede ser normal. En ese caso, comprueba el proxy mediante el transporte que utiliza la aplicación y sus registros autorizados. No cambies a una salida directa como supuesto arreglo.

### Una sonda HTTP con curl

Para la API directa de OpenAI, esta comprobación sin autenticación prueba la ruta hacia `/v1/models`. No comprueba tu clave ni solicita inferencia:

~~~bash
curl -q --connect-timeout 5 --max-time 15 \
  --output /dev/null \
  --write-out 'http=%{http_code} dns=%{time_namelookup} tcp=%{time_connect} tls=%{time_appconnect} total=%{time_total}\n' \
  https://api.openai.com/v1/models
resultado=$?
printf 'salida_curl=%s\n' "$resultado"
~~~

`-q` debe ser el primer argumento y evita cargar la configuración predeterminada de `curl`; **no elimina las variables de proxy**. Para Azure o una pasarela, utiliza una URL de comprobación documentada por ese proveedor y su configuración de red autorizada, sin enviar credenciales de OpenAI a un host distinto.

Interpreta por separado el estado HTTP y el código de salida. Un `401` con salida `0` significa que curl recibió una respuesta, no que la autenticación funcionó. Sin `--fail`, curl puede terminar con `0` aunque reciba un `429` o un `503`. `http=000` indica que la sonda no obtuvo un estado HTTP; no demuestra qué sucedió con otra petición de tu aplicación. Estas opciones y códigos aparecen en el [manual de curl](https://curl.se/docs/manpage.html).

| Salida de curl | Qué investigar |
| --- | --- |
| `5` o `6` | Resolución DNS del proxy o del host, respectivamente |
| `7` | Conexión TCP al destino o intermediario utilizado |
| `28` | Fase que agotó el tiempo; consulta también los tiempos y el error |
| `35`, `60` o `77` | Negociación TLS, confianza del certificado o fichero de autoridades |
| `52` o `56` | Respuesta vacía o fallo durante la recepción |
| `97` | Negociación con el proxy |

El código acota la investigación; no identifica por sí solo una causa única. Por ejemplo, un timeout puede producirse al conectar o al esperar una respuesta.

## Ramas de ruta: corrige la causa que has encontrado

| Resultado de la comprobación | Corrección condicionada | Cómo comprobarla |
| --- | --- | --- |
| El host no se resuelve dentro del contenedor | Corrige un host mal escrito o la configuración del resolvedor del despliegue | Vuelve a resolver el mismo host desde ese contenedor y repite la llamada instrumentada |
| DNS funciona, pero TCP es rechazado o agota el plazo | Revisa la salida HTTPS autorizada, el firewall, NAT y la disponibilidad del proxy con el responsable de red | Confirma conexión por la salida prevista; no basta con abrir un puerto en tu portátil |
| Falla la verificación del certificado | Instala o configura la cadena de confianza aprobada que necesita ese transporte, por ejemplo la CA corporativa de inspección TLS | Repite TLS con el mismo contexto y nombre de host; la validación debe seguir activada |
| curl funciona, pero el SDK falla | Compara proxy, CA, URL base y transporte efectivo del SDK | Repite desde el proceso con la configuración corregida, no solo desde la terminal |
| La respuesta empieza y luego se interrumpe | Revisa el timeout de lectura y los plazos del proxy, balanceador o función | Consume la respuesta completa con la operación original y registra dónde se corta |
| El error aparece solo al aumentar la concurrencia | Comprueba espera por conexiones del pool y límites de la salida de red | Reduce la concurrencia de forma controlada y observa si desaparece la espera; después ajusta el recurso identificado |

No desactives la verificación TLS para corregir un certificado ni fijes una IP para evitar el DNS. Ambas medidas cambian la prueba y pueden ocultar el problema que necesita reparar el despliegue.

### Python: configura el cliente HTTP de tu versión

La referencia actual utiliza **HTTPX2** y `DefaultHttpx2Client`; permite configurar proxy, transporte y un `ssl.SSLContext` con autoridades de confianza. Para separar fases, configura `httpx2.Timeout` con límites de conexión, lectura y escritura. Usa estas opciones únicamente si corresponden a la versión instalada de tu SDK. Algunas versiones anteriores utilizan **HTTPX**, `httpx.Timeout` y otro cliente predeterminado. No mezcles objetos de ambas bibliotecas ni copies nombres de la documentación actual sobre una instalación antigua. Consulta la sección de [cliente HTTP personalizado en Python](https://developers.openai.com/api/reference/python).

Reutiliza un cliente con una duración adecuada al proceso y ciérralo antes de terminar el bucle asíncrono. Si el fallo coincide con el cierre o la reutilización del cliente después de cerrarlo, corrige esa gestión antes de aumentar reintentos. Cambiar el código privado del SDK no es una configuración de transporte.

### Node.js: el proxy debe corresponder al fetch utilizado

No presupongas que el `fetch` de tu entorno utiliza las variables de proxy de la terminal. En Node, el ejemplo actual del SDK combina el `fetch` de **undici** con su `ProxyAgent` y pasa el `dispatcher` mediante `fetchOptions`. Este fragmento requiere `openai`, `undici` y una URL de proxy autorizada en `HTTPS_PROXY`; muestra la construcción del cliente, sin efectuar ninguna llamada:

~~~javascript
import OpenAI from "openai";
import { fetch as undiciFetch, ProxyAgent } from "undici";

const proxyUrl = process.env.HTTPS_PROXY;
if (!proxyUrl) throw new Error("Falta la URL del proxy autorizado");
const dispatcher = new ProxyAgent(proxyUrl);
const client = new OpenAI({
  fetch: undiciFetch,
  fetchOptions: { dispatcher },
  maxRetries: 0,
  timeout: 30_000,
});
~~~

El `dispatcher` no es una opción universal para cualquier implementación de `fetch`. En Bun y Deno, la configuración del proxy es distinta; sigue el apartado correspondiente de la [referencia de JavaScript](https://developers.openai.com/api/reference/typescript). Si tu entorno ya proporciona un transporte configurado, revisa ese transporte antes de sustituirlo.

### Endpoint directo, Azure y pasarela

Para OpenAI directo, confirma que utilizas la URL base de OpenAI. En Azure, comprueba el endpoint del recurso y la configuración que requiere tu modalidad de acceso. En una pasarela compatible, verifica su URL base y sus reglas de salida: la compatibilidad del formato no convierte su red, certificados o errores en los de OpenAI.

Una ruta alternativa que funciona puede recuperar temporalmente una tarea autorizada, pero **no prueba que la ruta original esté reparada**. Conserva ambas observaciones y vuelve a probar el destino original después de corregirlo. Si la llamada sale del navegador, investiga también sus restricciones y evita exponer claves API; el diagnóstico del proceso de servidor es una comprobación diferente.

## Comprueba la recuperación antes de reanudar el trabajo

Tras cambiar una sola causa identificada, envía una operación pequeña autorizada desde el mismo proceso, con la misma URL base, acceso, proyecto, endpoint y modelo que fallaban. Esta prueba de inferencia puede consumir saldo; una sonda sin autenticación no la sustituye.

La recuperación significa que **esa operación termina correctamente**, no solo que DNS responde o que recibes un `401`. Conserva su identificador cuando esté disponible. Si el fallo original era un stream, consume el stream hasta el final: recibir el primer fragmento no verifica la lectura completa. Después aumenta el tráfico gradualmente para comprobar si reaparece un problema de conexiones o de plazos.

Un timeout no justifica repetir a ciegas. Puede haberse enviado el cuerpo y perdido la respuesta. Tampoco vuelvas a ejecutar automáticamente un stream que ya produjo contenido o una operación que dispara acciones posteriores. La referencia actual de [Python](https://developers.openai.com/api/reference/python) indica que la lectura de streams puede generar errores y no se reproduce automáticamente al fallar. Decide cómo recuperar cada trabajo según su estado y su posibilidad de duplicación; un identificador de correlación no proporciona idempotencia.

## Paquete de escalación

Si persiste, envía al equipo de red o al proveedor la hora con zona horaria, host y endpoint sin secretos, SDK y versión, entorno y región, excepción y causa subyacente, estado HTTP cuando exista y resultados de las comprobaciones. Añade qué cambió entre el fallo y el intento siguiente. Consulta [OpenAI Status](https://status.openai.com/) para la hora del incidente cuando el destino sea OpenAI; el estado general no descarta un fallo concreto de tu ruta.

Para solicitudes a endpoints de OpenAI que admiten esta cabecera, como Responses, Chat Completions y Embeddings, puedes añadir un `X-Client-Request-Id` único, explícito, ASCII y de hasta 512 caracteres. Si un timeout impide obtener `x-request-id`, ese identificador puede ayudar a soporte a buscar si recibió la petición. La [referencia oficial de identificadores](https://developers.openai.com/api/reference/overview#supplying-your-own-request-id-with-x-client-request-id) documenta su utilidad y alcance. No garantiza deduplicación, devolución del cargo ni compatibilidad con todas las pasarelas.

## FAQ

### ¿APIConnectionError significa que OpenAI está caído?

No. También puede deberse al DNS, un certificado, un proxy o la salida de red de tu proceso. Identifica el destino y la causa encadenada, y consulta el estado del proveedor para la hora del fallo. Si ya recibiste un `503`, conserva esa respuesta y averigua quién la emitió.

### ¿Por qué no hay request ID? ¿Se ha cobrado la petición?

La conexión puede haber fallado antes de llegar a la API, pero también puede haberse perdido la respuesta después del envío. La ausencia de identificador **no prueba que la petición no se procesó ni que sea gratuita**. Conserva el estado del trabajo y, para OpenAI, utiliza el identificador de cliente documentado al investigar nuevos intentos; soporte y los registros disponibles deberán aclarar lo sucedido.

### ¿Debo cambiar la API key?

Un error de conexión por sí solo no indica que la clave sea incorrecta. Si recibes un `401` del endpoint previsto, investiga la autenticación. Si falla DNS o TLS, cambiar la clave no corrige esa fase.

### ¿Aumentar el timeout soluciona Connection error?

Solo puede ayudar si el plazo es insuficiente para una operación que sigue progresando y el resto de la ruta permite esa duración. No corrige un certificado inválido, una conexión rechazada o un proxy mal configurado. Distingue además los plazos del SDK, de tu aplicación y de los intermediarios; el más corto puede cortar la respuesta.

## Fuentes

Páginas externas que cita esta guía, en el orden en que aparecen. Última actualización: 2026-10-07.

- [referencia oficial de errores](https://developers.openai.com/api/docs/guides/error-codes) (developers.openai.com)
- [referencia actual del SDK de Python](https://developers.openai.com/api/reference/python) (developers.openai.com)
- [JavaScript](https://developers.openai.com/api/reference/typescript) (developers.openai.com)
- [manual de curl](https://curl.se/docs/manpage.html) (curl.se)
- [OpenAI Status](https://status.openai.com/) (status.openai.com)
- [referencia oficial de identificadores](https://developers.openai.com/api/reference/overview) (developers.openai.com)
