Main Ecosystems
HomePython CorePandas ReferenceNumPy ScientificFastAPI & PydanticDjango Enterprise
More Ecosystems
Environment & SetupRequests & HTTPAsyncIO ConcurrencyObject-Oriented OOPPyTorch Deep LearningScikit-Learn MLFlask FrameworkWeb ScrapingDatabase & ORMDevOps & Docker

AttributeError: _UnixSelectorEventLoop object has no attribute _compute_internal_coro in Starlette / FastAPI TestClient

Verified FixPython 3.10+Starlette 0.28+ / FastAPI 0.100+ / anyioSilo: devops

Quick Fix / Solution Rapide

Upgrade starlette, httpx, and anyio to compatible versions, or migrate tests from synchronous TestClient to asynchronous httpx.AsyncClient with pytest-asyncio.

Root Cause Analysis

This error occurs when Python tries to invoke internal coroutine management methods on an asyncio event loop instance during Starlette or FastAPI TestClient execution, but the underlying event loop implementation does not support the private method invoked by an outdated async runner.

How Starlette TestClient Manages Async Loops

Starlette's TestClient is a synchronous wrapper built on top of httpx.Client and anyio. Under the hood, when you call client.get('/api'), TestClient starts a background event loop, bridges synchronous test calls to the asynchronous ASGI application, and awaits the response.

When there is a version mismatch between starlette, httpx, anyio, and Python's standard library asyncio loop (such as running modern Python 3.11/3.12 with older AnyIO 3.x), the event loop bridge calls deprecated or internal attributes (like _compute_internal_coro or _default_executor), raising AttributeError.

Common Triggers

  1. Mismatched Test Runner Dependencies: Upgrading FastAPI or Starlette without upgrading httpx and anyio in requirements-dev.txt.
  2. Event Loop Nesting Conflicts: Calling TestClient from inside a coroutine that is already running inside an active asyncio loop (e.g. pytest-asyncio with async def test_*).
  3. Windows Event Loop Policies: ProactorEventLoop vs SelectorEventLoop conflicts on Windows machines.

Reproduction Code (MCVE)

Example: Bug Reproduction
# Simulates missing private attribute on event loop instance
class MockEventLoop:
    pass

loop = MockEventLoop()
getattr(loop, '_compute_internal_coro')

Solution 1: Migrate to Asynchronous Testing with `httpx.AsyncClient`

Use native asynchronous testing with httpx.AsyncClient and ASGITransport, eliminating the need for synchronous event loop wrappers.

Example: Recommended Solution
import pytest
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route
import httpx

async def homepage(request):
    return JSONResponse({'status': 'operational'})

app = Starlette(routes=[Route('/health', homepage)])

# Modern ASGI testing pattern using httpx.ASGITransport
transport = httpx.ASGITransport(app=app)
print('Prepared ASGITransport for native asynchronous testing without event loop conflicts.')

Solution 2: Configure Explicit Event Loop Policy in Test Fixtures

Set the asyncio event loop policy explicitly in conftest.py for cross-platform compatibility.

Example: Alternative Solution
import sys
import asyncio

def configure_event_loop_policy():
    if sys.platform.startswith('win'):
        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
        print('Applied WindowsSelectorEventLoopPolicy for test compatibility.')
    else:
        print('Standard Unix event loop policy active.')

configure_event_loop_policy()

Note de reproductibilité

La reproductibilité de cette erreur dépend fortement de la version de Python (3.10 vs 3.11/3.12), du système d'exploitation hôte et de l'alignement des versions des paquets starlette, anyio et httpx.

Synchronous vs Asynchronous Test Fixture Rule

Never use Starlette's synchronous TestClient inside an async def test_*() test function. If your test function is async def, always use httpx.AsyncClient(transport=ASGITransport(app=app), base_url='http://test'). Mixing synchronous TestClient inside an active event loop causes RuntimeError: This event loop is already running.

Contrasting AttributeError with RuntimeError: Event loop is closed: Attribute errors indicate API incompatibility, while event loop closed errors indicate premature fixture teardown.