Add error recovery mode - #70
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_recoveryparameter ("auto"by default, plus"manual"and"off").In
automode 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 todefault, 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.nodesandflow.edgeskeep every element, and the hidden ones reappear once the underlying state is repaired.If retries are exhausted (or in
manualmode) 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.reactflowlogger and emitted as a newclient_errorevent, so these failures land in the application log instead of vanishing into the browser console. Interaction handlers are wrapped too and report withsource="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
ReactFlowProviderbut outside the four<Panel>regions, so a canvas failure leavestop_panel,bottom_panel,left_panelandright_panelcontent 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_recoveryparameter,_handle_client_error, and theclient_errorevent.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 (aNoneposition, which makes React Flow dereferenceposition.x) through all three modes.docs/how-to/recover-from-errors.md, aclient_errorrow in the events table, andexamples/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.