Interrupciones de streaming y SSE: recuperación sin duplicados
Distingue cortes de SSE, conserva la salida parcial y reintenta con seguridad sin duplicar efectos secundarios.
Una petición de API en streaming solo termina cuando llega el evento terminal definido por el protocolo. Un socket cerrado, un timeout del cliente o el último fragmento de texto no prueban que haya terminado. Tras una interrupción, conserva los eventos recibidos, la Request ID y el estado de la operación. Antes de reintentar, comprueba los efectos secundarios: ningún protocolo permite reanudar universalmente desde el último token, y un retry ciego puede ejecutar una herramienta dos veces.
Final normal o corte real
Server-Sent Events envía una secuencia de eventos por una conexión HTTP larga. El cliente lee hasta uno de estos resultados:
completed confirma el éxito terminal; failed transmite un error dentro del stream; disconnected indica que el transporte acabó sin final confirmado. Este último estado requiere diagnóstico.
OpenAI Responses usa eventos como response.created, fragmentos de output y response.completed, además de estados de error o incompletos. Anthropic Messages usa message_start, eventos de bloques de contenido, message_delta y message_stop. No mezcles esos nombres en un mismo parser.
¿Quieres reproducir un corte de streaming con una petición controlada? Crea tu propia cuenta BetterToken y API Key, abre la referencia de API y empieza con un stream corto sin Tool Calls. Compara hora, modelo y estado con el Dashboard; añade un retry limitado solo después de comprobar el evento terminal y la salida parcial.
Datos que hay que guardar al cortarse la conexión
Un registro mínimo ayuda a separar un fallo del cliente de uno del servidor:
No guardes la API Key, el prompt completo, argumentos de herramientas ni respuestas sensibles. Guarda texto parcial solo si la política de la aplicación lo permite. En producción suelen servir más el hash de la operación, el número de eventos y el último marcador de secuencia seguro. La Request ID puede llegar en cabeceras HTTP o en un evento; guárdala al principio, no al terminar el stream.
Comprobar el parser antes de la red
El cliente debe manejar varias líneas data: en un evento, líneas vacías, fragmentos UTF-8 en límites de chunks, eventos desconocidos, errores después de HTTP 200, eventos terminales sin el último delta y argumentos de herramientas divididos. Un chunk TCP no equivale a un evento SSE: primero reconstruye una frame SSE completa y luego analiza el JSON.
Implementa is_terminal_success e is_terminal_failure por separado para Responses, Chat Completions y Messages. Tratar un evento desconocido como éxito oculta el estado que decide si es seguro reintentar.
Comprobar timeout en cada capa
Una conexión larga atraviesa el timeout del SDK o cliente HTTP, el timeout idle/read de la aplicación, reverse proxy, load balancer o ingress, proxy corporativo, red doméstica o móvil y el límite de generación del servidor. El timeout general y el idle timeout son parámetros distintos. Si el modelo envía eventos regularmente, no debería dispararse un idle timeout corto; si se aceptan pausas largas, el valor debe ajustarse a la carga esperada.
Revisa también el buffering del proxy. Si acumula chunks SSE, el usuario no ve texto durante mucho tiempo y luego recibe un bloque grande o un timeout. Mide tiempo hasta el primer evento e intervalos entre eventos en local, detrás del reverse proxy y en producción.
Salida parcial: mostrar, guardar o descartar
El texto parcial puede ser útil en la interfaz, pero su estado debe ser explícito. No presentes una respuesta cortada como completada.
streaming: el texto aún cambia;complete: llegó el evento terminal de éxito;partial: la conexión se perdió tras varios eventos;failed: el protocolo entregó un error;cancelled: usuario o aplicación detuvieron la petición.
Para partial, guarda el texto recibido separado de la nueva generación. Unir dos generaciones automáticamente es peligroso: el modelo puede repetir texto, reformularlo o llamar herramientas en otro orden.
Cuándo se puede repetir una petición
La seguridad del retry depende de la acción.
Texto sin acciones externas
Una petición de texto corta normalmente puede repetirse con intentos limitados. Muestra el resultado antiguo como partial y la respuesta nueva como generación independiente, o sustitúyela solo tras confirmación explícita.
Tool Calls y transacciones
Antes de reintentar, comprueba si la herramienta ya se ejecutó. Si el stream se corta después de enviar la orden, la segunda petición puede volver a crear una issue, un correo o una transacción de pago. Usa un Idempotency Key al nivel de herramienta, tu propia Operation ID y un registro de acciones terminadas.
Tarea de agente larga
«Continuar desde el último token» rara vez está confirmado por el protocolo. Restaura la tarea desde el estado de aplicación guardado: mensajes confirmados, resultados de herramientas y último paso terminado. Fragmentos de texto sin procesar no son un estado consistente del agente.
Retry limitado con backoff
La política de retry necesita un número finito de intentos:
Que un error sea retryable depende del error de protocolo, HTTP status, evento terminal y efectos secundarios. 401, un Model ID incorrecto o JSON inválido no se arreglan esperando. 429, un 5xx temporal o un fallo de transporte pueden permitir un retry limitado teniendo en cuenta las cabeceras del provider. El backoff no sustituye clasificar el error.
Prueba mínima
- Envía un stream corto sin herramientas.
- Anota todos los tipos de evento y espera el evento terminal.
- Corta el cliente artificialmente después de varios eventos.
- Comprueba que el resultado es
partial. - Prueba un único retry limitado.
- Repite detrás del proxy de producción.
- Compara ambas peticiones por hora, modelo y estado en Dashboard.
Consulta los tipos exactos en OpenAI Streaming Responses y Anthropic Messages Streaming.
FAQ
¿Se puede continuar un stream desde el último token?
No existe un mecanismo universal. Guarda el resultado parcial y el estado de aplicación, y sigue las capacidades de la API concreta. Una nueva petición puede repetir o cambiar el texto.
¿Por qué termina con error aunque HTTP status sea 200?
Las cabeceras llegan antes de la generación completa. El error puede aparecer después como evento de protocolo o corte de transporte; el status por sí solo no basta.
¿Hay que repetir tras cada desconexión?
No. Primero comprueba evento terminal, error, Request ID y efectos secundarios. Un retry de Tool Call exige idempotencia.
¿Dónde buscar si funciona en local?
Revisa reverse proxy, load balancer, idle timeout, buffering y red corporativa. Compara intervalos de eventos antes y después de cada capa.
¿Qué se debe comprobar en BetterToken?
Abre Dashboard y compara hora, modelo, estado y uso. Consulta parámetros actuales en la referencia de API; no envíes al soporte la API Key completa ni un prompt sensible.