fix(event): send a failed handler's writes after its last yield
FA
14h ago+173+173+012345678901234567890123456789-6-6-0123456789
Related to #7248. Pairs with #7412.
Problem
A foreground handler can change state after its last yield, for example in an except asyncio.CancelledError block when a supersedes=True call cancels it, or just before it raises. Nothing sends those changes to the frontend.
- Memory and disk send them late, with the next handler's first delta. The next handler gets the same state object, and that object still marks the vars as dirty.
- Redis without a cached lease (the default,
REFLEX_OPLOCK_ENABLED=False) never sends them. The next handler unpickles a fresh copy, and_init_bookkeepingclears the dirty marks. The frontend shows the old value until the var changes again or the page reloads.
Fix
When process_event raises or is cancelled, _execute_event flushes the delta with chain_updates(None, root_state=...) before the exception propagates. The flush runs under the state lock, so it reaches the frontend before any delta from the next handler. If the flush fails, it is logged and the handler's own exception propagates. The background branch uses the same pattern.
Background handlers are not changed. They already flush in their finally.
Tests
test_failed_handler_emits_writes_after_last_yield: memory, disk and redis, each cancelled and raising. All 6 cases fail on main and pass with the fix.test_failed_handler_flush_error_keeps_handler_exception: a computed var makes the flush fail. The handler'sRuntimeErroris reported and the flush error is logged. It fails when theexceptin_flush_failed_handleris removed.