Skip to content

Add error recovery mode - #70

Merged
philippjfr merged 1 commit into
mainfrom
error_recovery
Aug 21, 2026
Merged

Add error recovery mode#70
philippjfr merged 1 commit into
mainfrom
error_recovery

Conversation

@philippjfr

Copy link
Copy Markdown
Contributor

Problem

A React rendering error is unforgiving: when a component throws during render, React unmounts the whole subtree. In a graph editor that means one malformed node blanks the canvas, and because the exception dies in the browser console the server never learns about it. The user is left with an empty viewport and no way back other than reloading, even though the graph is still safely held in Python. Users who have spent time building out a large graph reasonably assume their work is gone.

What this adds

An error boundary around the canvas, controlled by a new error_recovery parameter ("auto" by default, plus "manual" and "off").

In auto mode a render failure remounts the canvas once, then remounts it a second time in safe mode, where the graph is validated before it reaches React Flow. Safe mode repairs what it can (non-finite positions reset to the origin, unregistered node types fall back to default, unknown edge types are stripped) and hides what it cannot render (dangling edges, duplicate or missing ids, non-object elements). A banner reports exactly what was repaired and what was hidden, and states that nothing was deleted on the server.

Safe mode is view-only by construction: the frontend never pushes a full graph sync to Python, only per-event messages, so filtering what gets handed to React Flow cannot mutate server-side state. flow.nodes and flow.edges keep every element, and the hidden ones reappear once the underlying state is repaired.

If retries are exhausted (or in manual mode) the canvas is replaced by a recovery panel naming the error, with Try again, Reload page and Copy details actions. Because Python holds the canonical graph, the panel can honestly tell the user that reloading loses no work. The retry budget refills after a remounted canvas survives for five seconds, so a graph that breaks again much later still gets fresh attempts.

Every caught error is reported back to Python, logged to the panel.reactflow logger and emitted as a new client_error event, so these failures land in the application log instead of vanishing into the browser console. Interaction handlers are wrapped too and report with source="handler", which catches the case where a drag or connect throws and leaves the canvas showing a change that never reached the server.

The boundary sits inside ReactFlowProvider but outside the four <Panel> regions, so a canvas failure leaves top_panel, bottom_panel, left_panel and right_panel content mounted and usable during recovery.

Changes

  • src/panel_reactflow/models/reactflow.jsx: FlowErrorBoundary, RecoveryOverlay, SafeModeBanner, sanitizeGraph, the retry state machine, and error reporting for interaction handlers.
  • src/panel_reactflow/base.py: error_recovery parameter, _handle_client_error, and the client_error event.
  • src/panel_reactflow/dist/css/reactflow.css: styles for the overlay and banner, using the existing Panel CSS variables so they follow light and dark themes.
  • tests/test_error_recovery.py: 9 tests for reporting, logging and the guarantee that a client error never mutates the graph.
  • tests/ui/test_error_recovery.py: 7 Playwright tests driving the real failure (a None position, which makes React Flow dereference position.x) through all three modes.
  • docs/how-to/recover-from-errors.md, a client_error row in the events table, and examples/error_recovery.py.

Verification

116 Python tests pass, all 7 Playwright tests pass, and the example was checked end to end in a browser: breaking a node position escalates to safe mode with every node still rendered, a dangling edge is hidden while the server keeps it, and repairing the state clears both the banner and the reported errors.

AI Disclosure

Written with heavy assistance from Claude Opus 5.

@philippjfr
philippjfr merged commit 55eb0cf into main Aug 21, 2026
31 of 32 checks passed
@philippjfr
philippjfr deleted the error_recovery branch August 21, 2026 14:46
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