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
- Move the substates/parent_state access inside an `async with self:` block
- 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
- 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
- Do all substates/parent_state navigation inside `async with self`
- Get sibling states via the state manager instead of walking the tree from a background proxy
- Keep tree-navigation logic out of background tasks where possible
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
- The state is already mutable. Do not nest `async with self`
- @rx.event(background=True) is required for upload_files_chun
- Background task must use `async with self` to modify state.
- Cannot suppress RuntimeError exceptions which may be raised
- ChildrenTypeError(component=cls.__name__, child=child)
AI-assisted analysis of reflex-dev/reflex@45b8ed5ab7 (2026-08-28).
Data as JSON: /api/errors/bd99cc362a14932d.
Report an issue: GitHub.