reflex-dev/reflex · error · RuntimeError

Cannot directly call background task {name!r}, use `yield {t

Error message

Cannot directly call background task {name!r}, use `yield {type(state).__name__}.{name}` or `return {type(state).__name__}.{name}` instead.

What it means

Reflex background tasks (@rx.background) cannot be chained by calling them directly like normal event handlers, because they run in a separate task with their own state snapshot. Calling such a coroutine raises RuntimeError with a message telling you to yield or return the handler reference instead. The wrapper _no_chain_background_task_co replaces the original async function.

Source

Thrown at reflex/state.py:127

        state: The state instance the background task is bound to.
        name: The name of the background task.
        fn: The background task coroutine function / generator.

    Returns:
        A compatible coroutine function / generator that raises a runtime error.

    Raises:
        TypeError: If the background task is not async.
    """
    call = f"{type(state).__name__}.{name}"
    message = (
        f"Cannot directly call background task {name!r}, use "
        f"`yield {call}` or `return {call}` instead."
    )
    if inspect.iscoroutinefunction(fn):

        async def _no_chain_background_task_co(*args, **kwargs):  # noqa: RUF029
            raise RuntimeError(message)

        return _no_chain_background_task_co
    if inspect.isasyncgenfunction(fn):

        async def _no_chain_background_task_gen(*args, **kwargs):  # noqa: RUF029
            yield
            raise RuntimeError(message)

        return _no_chain_background_task_gen

    msg = f"{fn} is marked as a background task, but is not async."
    raise TypeError(msg)


def _substate_key(
    token: str,
    state_cls_or_name: BaseState | type[BaseState] | str | Sequence[str],
) -> str:

View on GitHub (pinned to 45b8ed5ab7)

Solutions

  1. Replace the direct call with `yield self.my_bg_task` (in a generator handler) or `return self.my_bg_task` (in a regular handler)
  2. Ensure the calling event handler yields/returns the background handler so Reflex schedules it correctly
  3. Pass any arguments via the yielded handler: `yield self.my_bg_task(arg1)` only if the API supports it — otherwise store args in state first

Example fix

# before
async def do_work(self):
    await self.my_bg_task()  # RuntimeError

# after
@rx.background
async def my_bg_task(self):
    async with self:
        ...

def do_work(self):
    yield self.my_bg_task
Defensive patterns

Strategy: validation

Validate before calling

import inspect

def is_background_task(fn) -> bool:
    return getattr(fn, '_reflex_background', False) or fn.__name__.startswith('_no_chain_background_task')

Type guard

def is_background_task_handler(fn) -> bool:
    return fn.__name__ in ('_no_chain_background_task_co', '_no_chain_background_task_gen')

Prevention

When it happens

Trigger: Inside an event handler, calling `self.my_bg_task()` directly where my_bg_task is decorated with @rx.background and defined as an async def (coroutine).

Common situations: Refactoring a normal async event handler into a background task and forgetting to update call sites; new developers treating background tasks like regular methods.

Related errors


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