Raise when an event handler is awaited outside any client's event
All Submissions:
- Have you followed the guidelines stated in CONTRIBUTING.md file?
- Have you checked to ensure there aren't any other open Pull Requests for the desired changed?
Found in the reflex 0.11.0a1 pre-release testing (FINDING-010).
Stacked on #7796 (merge that first). Until #7796 merges, this PR's diff also contains its commits. The change here is the last commit, 83a304318.
Type of change
- Bug fix (non-breaking change which fixes an issue)
Changes To Core Features:
- Have you added an explanation of what your changes do and why you'd like us to include them?
- Have you written new tests for your core changes, as applicable?
- Have you successfully ran tests with your changes locally?
What was wrong. _RootMiddleware attaches the event processor's root EventContext to every request, and that context's token is "". execute_event_inline only raised when no context existed at all. So an API route that ran await State.handler() outside app.modify_state enqueued the event under the empty token. Every caller of that route shared one state: in the pre-release run, a counter route returned 1, 2, 3, 4 to callers with different tokens, and each caller read the previous caller's stored value, also across workers on prod + Redis. Browser sessions were not exposed, because a socket is never linked to an empty token.
The fix. A context without a token now counts as no context. Awaiting raises the existing EventHandlerTypeError: "Event handler … can only be awaited from an event handler or inside app.modify_state; use it as an event trigger or yield it instead." Inside app.modify_state, the context is forked to the token being modified, so awaiting there still works. That matches the rule docs/events/chaining_events.md already states ("awaiting a handler anywhere else raises an error"). The docs bullet now names the API-route case.
Tests (tests/units/reflex_base/event/processor/test_base_state_processor.py):
test_await_from_a_request_outside_any_event_raises: a request goes through_RootMiddlewarewith the real processor's root context.await State.add(1)raises, and the empty-token state is unchanged. This test failed before the fix.test_await_inside_app_modify_state_runs_on_that_token: awaiting insideapp.modify_state(BaseStateToken(...))returns the handler's values and writes to that token's state.- The existing tests still cover awaiting inside a foreground or background event handler.
Checks:
uv run pytest tests/units --cov(without the Chromium-onlyreflex_bench/drivers/test_browser.py): 15665 passed, 66 skipped, 81.44% coverage.ruff checkandruff formatare clean.pyright reflex testsreports 0 errors.
Generated by Claude Code