Tecnología · GUÍA PRÁCTICA
¿asyncio.run() falla en Jupyter o FastAPI? Corrige al dueño del bucle, no el síntoma
La excepción avisa de un conflicto de propiedad: algo ya ejecuta el bucle en este hilo. Espera dentro de ese dueño o crea un solo bucle en la verdadera entrada síncrona.

Python lanza este error antes de iniciar un segundo bucle en el mismo hilo. Es intencional: asyncio.run() posee la creación, ejecución, limpieza de generadores asíncronos, cierre del ejecutor y cierre del bucle. Un kernel de Notebook o un servidor web ya posee esas responsabilidades.
La reparación segura depende del llamador. En una celda o ruta asíncrona, espera la corrutina. En un script independiente, conserva un solo asyncio.run() en el límite del proceso. En una biblioteca reutilizable, expón una API asíncrona y haz que el envoltorio síncrono falle si ya existe un bucle.
Nombra al dueño antes de cambiar código
Registra el hilo, el resultado de asyncio.get_running_loop() y el host. Una celda, un manejador web, un gancho de ciclo de vida y una entrada CLI tienen contratos distintos.
No llames stop(), close(), run_until_complete() ni asyncio.run() sobre un bucle que no creaste.
Usa await dentro de notebooks y marcos asíncronos
En Jupyter cambia result = asyncio.run(work()) por result = await work(). En FastAPI el llamador asíncrono debe esperar dentro del bucle del marco.
Vincula las Task largas al ciclo de vida del host y define inicio, cancelación y cierre.
Conserva un único dueño superior en un script
La entrada real puede usar asyncio.run(main()); si varias llamadas comparten bucle y contexto, usa un solo asyncio.Runner en secuencia.
No repartas runners entre auxiliares; la implementación interior debe seguir siendo esperable.
Separa los contratos asíncrono y síncrono
Implementa async def fetch_async(...). El envoltorio fetch(...) solo usa asyncio.run() sin un bucle activo; si lo hay, debe indicar await fetch_async.
Comprueba el bucle antes de construir la corrutina y cierra explícitamente cualquier corrutina sin usar.
Envía entre hilos solo con un límite real
Para un bucle en otro hilo, asyncio.run_coroutine_threadsafe(coro, loop) devuelve un Future; define también tiempos, cancelación y cierre.
Esperar future.result() desde el hilo del bucle puede causar interbloqueo.
Limita los parches a una capa de compatibilidad
Si son necesarios, limita alcance y versión y pruébalos como adaptador; no los uses como valor global predeterminado.
Un valor devuelto no prueba integridad: pueden quedar errores de contexto, tareas pendientes o fallos posteriores.
Demuestra el límite reparado
Prueba la API asíncrona bajo un bucle activo y el envoltorio síncrono en un hilo normal; verifica valores, errores, cancelación, contextvars, repeticiones y asyncio.all_tasks().
La prueba negativa debe exigir que el envoltorio síncrono falle de inmediato dentro del bucle y recomiende await.
