{"record":{"id":"28de64e72132345c","repo":"reflex-dev/reflex","slug":"the-state-is-already-mutable-do-not-nest-async-w","errorCode":null,"errorMessage":"The state is already mutable. Do not nest `async with self` blocks.","messagePattern":"The state is already mutable\\. Do not nest `async with self` blocks\\.","errorType":"exception","errorClass":"ImmutableStateError","httpStatus":null,"severity":"error","filePath":"reflex/istate/proxy.py","lineNumber":148,"sourceCode":"\n            parent_state = (\n                await self._self_parent_state_proxy.__aenter__()\n            ).__wrapped__\n            super().__setattr__(\n                \"__wrapped__\",\n                await parent_state.get_state(\n                    State.get_class_substate(self._self_substate_path)\n                ),\n            )\n            self._self_entered_context = True\n            return self\n        current_task = asyncio.current_task()\n        if (\n            self._self_actx_lock.locked()\n            and current_task == self._self_actx_lock_holder\n        ):\n            msg = \"The state is already mutable. Do not nest `async with self` blocks.\"\n            raise ImmutableStateError(msg)\n\n        ctx = EventContext.get()\n\n        await self._self_actx_lock.acquire()\n        try:\n            self._self_actx_lock_holder = current_task\n            self._self_actx = ctx.state_manager.modify_state_with_links(\n                token=self._self_substate_token, event=self._self_event\n            )\n            mutable_state = await self._self_actx.__aenter__()\n            self._self_mutable = True\n            self._self_entered_context = True\n            super().__setattr__(\n                \"__wrapped__\", mutable_state.get_substate(self._self_substate_path)\n            )\n        except (Exception, asyncio.CancelledError):\n            # Restore the proxy to a consistent state since __aexit__ will not be called when __aenter__ raises.\n            await self.__aexit__(*sys.exc_info())","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/reflex-dev/reflex/blob/45b8ed5ab735f8a56bbb09a42384f030eb0208e7/reflex/istate/proxy.py#L130-L166","documentation":"Inside a background (@rx.event(background=True)) task, state mutability is guarded by an async context lock held by the current task. Re-entering `async with self` while the same task already holds the lock is a programming error (the state is already mutable), so ImmutableStateError is raised instead of deadlocking on a non-reentrant lock.","triggerScenarios":"Nesting `async with self:` blocks inside one background event handler, e.g. calling a helper that also does `async with self` from inside an existing `async with self` block in the same task.","commonSituations":"Refactoring background handlers into helper functions that each open their own context; copy-pasting a mutation block into an already-mutable region; awaiting a utility coroutine that internally enters the context.","solutions":["Flatten to a single `async with self` block per task and do all mutations inside it","Make helpers take the already-mutable state (call them inside the context) rather than opening their own","If separate mutation windows are genuinely needed, exit the first context before entering the second"],"exampleFix":"# before\nasync with self:\n    self.a = 1\n    async with self:  # ImmutableStateError\n        self.b = 2\n\n# after\nasync with self:\n    self.a = 1\n    self.b = 2","handlingStrategy":"type-guard","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["One `async with self` block per background task; helpers receive already-mutable state","Never write helpers that open their own context when called from within one","Code-review background handlers for nested context entries"],"tags":["reflex","background-task","state-proxy","nested-context","deadlock-prevention"],"backgroundTag":"non-reentrant-lock","analyzedSha":"45b8ed5ab735f8a56bbb09a42384f030eb0208e7","analyzedAt":"2026-08-28T19:25:27.644Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}