aio-libs/aiohttp · error · RuntimeError
Timeout context manager should be used inside a task
Error message
Timeout context manager should be used inside a task
What it means
BaseTimerContext.__enter__ raises RuntimeError when asyncio.current_task() returns None — meaning the timeout context manager is being used outside an asyncio task. The timeout() helper relies on task-level cancellation mechanics, so it requires an active task in the event loop.
Solutions
- Ensure the timeout context manager is used inside an async function that runs as a task
- Wrap synchronous callbacks in a coroutine before using timeout
- Use asyncio.run() or loop.run_until_complete() to establish a task context
Example fix
# before (no task context)
def callback():
with timeout(10):
do_work() # RuntimeError
# after (inside a task)
async def fetch():
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session:
return await session.get(url)
asyncio.run(fetch()) Defensive patterns
Strategy: validation
Validate before calling
import asyncio, sys
def ensure_task_context():
if sys.version_info >= (3, 11):
task = asyncio.current_task()
else:
task = asyncio.current_task()
if task is None:
raise RuntimeError('timeout() must be called from within an asyncio task')
return task Try / catch
try:
with timeout(10):
await do_work()
except RuntimeError as e:
if 'inside a task' in str(e):
# Re-run within a proper task
await asyncio.ensure_future(do_work_with_timeout())
raise Prevention
- Always use timeout() inside async functions that are awaited as tasks
- Never call timeout() from synchronous callbacks or fixtures
- Use pytest-asyncio or asyncio.run() to ensure task context in tests
When it happens
Trigger: Using aiohttp.timeout() or the internal TimerContext in synchronous code, in a callback scheduled with loop.call_soon (which has no task), or before the event loop is running. Also triggered when using timeout in a thread that has no asyncio task context.
Common situations: Calling client code with timeout in a non-async function; using timeout in asyncio.ensure_future callbacks that are not wrapped in a coroutine; testing async code with pytest but calling timeout synchronously in a fixture; calling from a sync callback registered via add_done_callback.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Cannot initialize a TLS-in-TLS connection to host
- Concurrent call to receive() is not allowed
- Connection timeout to host
- Failed to get module name.
- Multiple errors on cleanup stage
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/f8e9237d2fd5f959.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/helpers.py:705
"""Low resolution timeout context manager"""
__slots__ = ("_loop", "_tasks", "_cancelled", "_cancelling")
def __init__(self, loop: asyncio.AbstractEventLoop) -> None:
self._loop = loop
self._tasks: list[asyncio.Task[Any]] = []
self._cancelled = False
self._cancelling = 0
def assert_timeout(self) -> None:
"""Raise TimeoutError if timer has already been cancelled."""
if self._cancelled:
raise asyncio.TimeoutError from None
def __enter__(self) -> BaseTimerContext:
task = asyncio.current_task(loop=self._loop)
if task is None:
raise RuntimeError("Timeout context manager should be used inside a task")
if sys.version_info >= (3, 11):
# Remember if the task was already cancelling
# so when we __exit__ we can decide if we should
# raise asyncio.TimeoutError or let the cancellation propagate
self._cancelling = task.cancelling()
if self._cancelled:
raise asyncio.TimeoutError from None
self._tasks.append(task)
return self
def __exit__(
self,
exc_type: type[BaseException] | None,
exc_val: BaseException | None,
exc_tb: TracebackType | None,View on GitHub (pinned to d041d4d0fd)