reflex-dev/reflex · error · RuntimeError

Cannot suppress RuntimeError exceptions which may be raised…

Error message

Cannot suppress RuntimeError exceptions which may be raised by asyncio machinery.

What it means

Raised by `reflex.utils.tasks.ensure_task` when the caller passes RuntimeError in `suppress_exceptions`. asyncio machinery itself uses RuntimeError to signal loop-closed or no-running-loop conditions, so suppressing it would mask lifecycle bugs and can hang the owner forever. The guard rejects this explicitly before creating the background task.

Solutions

  1. Remove RuntimeError from the suppress_exceptions list and handle it explicitly (e.g. let the task die or catch it inside the coroutine and log/restart).
  2. If the RuntimeError comes from calling asyncio APIs on a closed loop, fix the shutdown ordering instead of suppressing.
  3. Suppress only the specific exception types your task can safely ignore (e.g. ConnectionError, TimeoutError).

Example fix

# before
ensure_task(poll, self, "_poll_task", suppress_exceptions=[RuntimeError, ValueError])

# after
ensure_task(poll, self, "_poll_task", suppress_exceptions=[ValueError])
Defensive patterns

Strategy: validation

Validate before calling

def safe_suppress(excs: list[type[BaseException]]) -> list[type[BaseException]]:
    if RuntimeError in excs:
        raise ValueError("Reflex forbids suppressing RuntimeError in ensure_task")
    return excs

Try / catch

try:
    ensure_task(coro, owner, attr, suppress_exceptions=[ConnectionError])
except RuntimeError as e:
    if "Cannot suppress RuntimeError" in str(e):
        # remove RuntimeError from list and retry without it
        ...

Prevention

When it happens

Trigger: Calling `ensure_task(coro, owner, task_attribute, suppress_exceptions=[RuntimeError, ...])` (or with a tuple containing RuntimeError) in any code that starts long-running Reflex background tasks (e.g. _ensure_lock_task, _ensure_socket_record_task, ensure_lost_and_found_task).

Common situations: A user-written background task raises RuntimeError and the developer blanket-suppresses Exception (RuntimeError included) to stop log spam; upgrading Reflex where the suppression API is now validated.

Related errors


AI-assisted analysis of reflex-dev/reflex@45b8ed5ab7 (2026-08-28). Data as JSON: /api/errors/9800b6b10d596df3. Report an issue: GitHub.

Appendix: source

Thrown at reflex/utils/tasks.py:95

        owner: The owner of the task.
        task_attribute: The attribute name to store/retrieve the task from the owner object.
        coro_function: The coroutine function to run as a task.
        suppress_exceptions: The exceptions to log and continue when running the coroutine.
        exception_delay: The delay between retries when an exception is suppressed.
        exception_limit: The maximum number of suppressed exceptions within the limit window before raising.
        exception_limit_window: The time window in seconds for counting suppressed exceptions.
        task_context: The context to use for the task.
        *args: The arguments to pass to the coroutine function.
        **kwargs: The keyword arguments to pass to the coroutine function.

    Returns:
        The asyncio task running the coroutine function.
    """
    if suppress_exceptions is None:
        suppress_exceptions = []
    if RuntimeError in suppress_exceptions:
        msg = "Cannot suppress RuntimeError exceptions which may be raised by asyncio machinery."
        raise RuntimeError(msg)

    task = getattr(owner, task_attribute, None)
    if task is None or task.done():
        asyncio.get_running_loop()  # Ensure we're in an event loop.
        rf_coro = _run_forever(
            coro_function,
            *args,
            suppress_exceptions=suppress_exceptions,
            exception_delay=exception_delay,
            exception_limit=exception_limit,
            exception_limit_window=exception_limit_window,
            **kwargs,
        )
        task_name = f"reflex_ensure_task|{type(owner).__name__}.{task_attribute}={coro_function.__name__}|{time.time()}"
        if task_context is not None:
            # Run the task in the given context (not needed after Python 3.11+ which supports passing context to create_task directly).
            task = task_context.run(asyncio.create_task, rf_coro, name=task_name)
        else:

View on GitHub (pinned to 45b8ed5ab7)