reflex-dev/reflex · error · ImmutableStateError

Background task StateProxy is immutable outside of a context

Error message

Background task StateProxy is immutable outside of a context manager. Use `async with self` to modify state.

What it means

In a background task, the StateProxy is immutable outside an `async with self` block. Accessing the special attributes substates or parent_state while immutable raises ImmutableStateError, because traversing the state tree could bypass mutation guards (a substate proxy obtained this way would appear mutable). Regular attribute reads are wrapped in immutable proxies; these two tree-navigation attributes are blocked outright.

Source

Thrown at reflex/istate/proxy.py:236

    def __getattr__(self, name: str) -> Any:
        """Get the attribute from the underlying state instance.

        Args:
            name: The name of the attribute.

        Returns:
            The value of the attribute.

        Raises:
            ImmutableStateError: If the state is not in mutable mode.
        """
        if name in ["substates", "parent_state"] and not self._is_mutable():
            msg = (
                "Background task StateProxy is immutable outside of a context "
                "manager. Use `async with self` to modify state."
            )
            raise ImmutableStateError(msg)

        value = super().__getattr__(name)  # pyright: ignore[reportAttributeAccessIssue]
        if not name.startswith("_self_") and isinstance(value, MutableProxy):
            # ensure mutations to these containers are blocked unless proxy is _mutable
            return ImmutableMutableProxy(
                wrapped=value.__wrapped__,
                state=self,
                field_name=value._self_field_name,
            )
        if isinstance(value, functools.partial) and value.args[0] is self.__wrapped__:
            # Rebind event handler to the proxy instance
            value = functools.partial(
                value.func,
                self,
                *value.args[1:],
                **value.keywords,
            )
        if isinstance(value, MethodType) and value.__self__ is self.__wrapped__:

View on GitHub (pinned to 45b8ed5ab7)

Solutions

  1. Move the substates/parent_state access inside an `async with self:` block
  2. To access a different state from a background task, obtain it via the state manager (e.g. app.state_manager.modify_state(BaseStateToken(...))) or pass it in, rather than walking the tree
  3. Restructure so the background task operates only on its own state fields

Example fix

# before
@rx.event(background=True)
async def handler(self):
    sibling = self.parent_state.substates["other"]  # ImmutableStateError

# after
@rx.event(background=True)
async def handler(self):
    async with self:
        sibling = self.parent_state.substates["other"]
        sibling.value = 1
Defensive patterns

Strategy: type-guard

Type guard

# inside a background task, check mutability before tree access
if proxy._is_mutable():
    sub = proxy.parent_state.substates  # ok inside `async with self`
else:
    # defer until inside `async with self`

Try / catch

from reflex.istate.proxy import ImmutableStateError
try:
    subs = self.substates
except ImmutableStateError:
    async with self:
        subs = self.substates  # now safe

Prevention

When it happens

Trigger: Inside @rx.event(background=True), reading self.substates or self.parent_state outside any `async with self:` block — e.g. logging the tree, finding a sibling state via self.parent_state.substates[...] to mutate it.

Common situations: Trying to reach another substate from a background handler by walking parent_state/substates instead of using app.state_manager or rx.app.get_state refactors; debugging code that prints self.substates; moving tree-navigation logic that worked in normal handlers into background tasks.

Related errors


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