Skip to content

Fast renderer: do not lose handler and cleanup errors - #64

Merged
maartenbreddels merged 1 commit into
masterfrom
fix/fast-lost-errors-and-flip-crash
Sep 29, 2026
Merged

maartenbreddels merged 1 commit into
masterfrom
fix/fast-lost-errors-and-flip-crash

Conversation

@maartenbreddels

@maartenbreddels maartenbreddels commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

We want to turn on REACTON_FAST=1 in production, so we audited the fast renderer against the default renderer: a line-by-line review, a fuzzer that compares both renderers (about 18,000 random trees), solara 1.60.2's test suite in both modes, and a production app's test suite. This PR fixes the remaining fast-only bugs that matter. The default renderer is unchanged.

Rebased on #63, which fixed the crash for a replaced parent widget in the same way. This PR is now smaller.

Fixes

  • Lost event handler exceptions. A handler (on_click, use_event) that raised while another thread was rendering lost its exception. force_update() only set a flag that the running render then cleared, and nothing marked the path to the component, so later renders skipped it. The handler now marks the ancestors, the same way a state setter does.
  • Lost effect cleanup exceptions. A cleanup that raised during unmount (for example after use_exception replaced a subtree) lost its exception, and closed widgets stayed on screen. Marking the ancestors stopped at a stale flag inside the subtree being removed. It now always walks up to the root, which costs only the tree depth.
  • A replaced parent with a shared element. The Fuzz the fast renderer against the default one, and fix the KeyError it finds #63 fix did not cover a widget replaced by a .shared() component element. Its arguments are rendered in the same context, so it still raised KeyError. Widget arguments and shared-element arguments now go through one helper (_render_arguments), which uses the _replacing counter from Fuzz the fast renderer against the default one, and fix the KeyError it finds #63.

Tests

  • Three new tests in core_test.py. CI runs them with REACTON_FAST=0 and =1. All three pass on the default renderer. On current master they fail on the fast renderer.
  • The threaded test checks that the click really happened during the render. It passed 30 of 30 runs in each mode.
  • The fuzzer from the audit found no new differences, and the benchmarks do not change.

Astra (GPT-6) reviewed the version before the rebase and approved it, with nothing blocking.

Not in this PR

These fast-mode differences remain, and are deliberate or rare:

  • Traits the frontend changed without an on_<trait> handler are no longer reset by unrelated renders.
  • Effect cleanup order when one component replaces another.
  • force_update() no longer re-applies element kwargs.

A keyed child that moves out of a parent that is being replaced shows a closed widget in both renderers. That is a separate bug.

🤖 Generated with Claude Code

An audit of REACTON_FAST=1 against the default renderer (fuzzing both
renderers, solara's test suite, and a production app) found fast-only bugs
that the default renderer does not have:

- An event handler exception raised while another thread renders was lost:
  force_update only set a flag that the running render cleared, and no path
  to the component was marked, so later renders skipped it.
- An effect cleanup exception during unmount was lost, and closed widgets
  stayed on screen: marking the ancestors stopped at a stale flag inside the
  subtree being removed, so the live ancestors were never marked. Walking to
  the root costs only the tree depth.
- The fix for a replaced parent widget did not cover a widget replaced by a
  shared component element, whose arguments are rendered in the same
  context: that still crashed with a KeyError. Both now share one helper.

The default renderer is unchanged by this; the new tests run in both modes
in CI and fail on the fast renderer without the fix.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@maartenbreddels
maartenbreddels force-pushed the fix/fast-lost-errors-and-flip-crash branch from 27b14f4 to eeda41a Compare September 28, 2026 16:00
@maartenbreddels maartenbreddels changed the title Fast renderer: no lost errors, no crash when a wrapper widget changes type Fast renderer: do not lose handler and cleanup errors Sep 28, 2026
@maartenbreddels
maartenbreddels merged commit 6bdf031 into master Sep 29, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant