python/cpython · warning · RuntimeError
capture_call_graph() is called outside of a running event lo
Error message
capture_call_graph() is called outside of a running event loop and no *future* to introspect was provided
What it means
asyncio.graph.capture_call_graph() introspects the current running task's call stack. Without a future argument it needs a running event loop to find tasks.current_task(); called from synchronous code it raises this RuntimeError telling you to either call it inside the loop or pass an explicit future to introspect.
Source
Thrown at Lib/asyncio/graph.py:135
``abs(limit)`` entries. If 'limit' is positive, the entries left are
the closest to the invocation point. If 'limit' is negative, the
topmost entries are left. If 'limit' is omitted or None, all entries
are present. If 'limit' is 0, the call stack is not captured at all,
only "awaited by" information is present.
"""
loop = events._get_running_loop()
if future is not None:
# Check if we're in a context of a running event loop;
# if yes - check if the passed future is the currently
# running task or not.
if loop is None or future is not tasks.current_task(loop=loop):
return _build_graph_for_future(future, limit=limit)
# else: future is the current task, move on.
else:
if loop is None:
raise RuntimeError(
'capture_call_graph() is called outside of a running '
'event loop and no *future* to introspect was provided')
future = tasks.current_task(loop=loop)
if future is None:
# This isn't a generic call stack introspection utility. If we
# can't determine the current task and none was provided, we
# just return.
return None
if not isinstance(future, futures.Future):
raise TypeError(
f"{future!r} object does not appear to be compatible "
f"with asyncio.Future"
)
call_stack: list[FrameCallGraphEntry] = []
View on GitHub (pinned to bc6749cc3b)
Solutions
- Call capture_call_graph() from inside a coroutine/callback running on the loop
- Or pass future=<the Task/Future under inspection> explicitly
- Capture the graph eagerly while the task is alive, store it, and report later from sync code
Example fix
# before
def sync_error_handler():
g = asyncio.graph.capture_call_graph() # RuntimeError
# after
async def task_body():
g = asyncio.graph.capture_call_graph() # inside running loop
def sync_error_handler(saved_graph):
report(saved_graph) Defensive patterns
Strategy: validation
Validate before calling
import asyncio
if asyncio.events._get_running_loop() is None and future is None:
# no loop and no future: capture_call_graph() will raise
graph = None Type guard
def can_capture_call_graph(future=None) -> bool:
import asyncio
return future is not None or asyncio.events._get_running_loop() is not None Prevention
- Call capture_call_graph() from inside coroutines
- Or pass the target future explicitly
- Capture graphs eagerly while tasks are alive; report later
When it happens
Trigger: Calling capture_call_graph() from a plain function during import, from a thread, or after the loop stopped, without the future parameter.
Common situations: Diagnostics/error reporters hooked into sync exception handlers; logging pipelines that try to attach asyncio context; calling from __del__ or signal handlers.
Related errors
- {htmldir!r} is not a Sphinx HTML output directory
- {name} is not part of stable ABI. Document it as `c:macro::`
- deprecated-removed:: second argument cannot be `next`
- no running event loop
- There is no current event loop in thread %r.
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/65dbba2a06947978.
Report an issue: GitHub.