Zero React error boundaries — any render exception in any component takes the whole app down with no recovery affordance #153
Labels
No labels
blocked:commit-ready-failed
blocked:gate-fetch
blocked:gate-lint
blocked:gate-plan
blocked:gate-research
blocked:needs-attention
blocked:needs-decision
blocked:needs-human
blocked:no-changes
blocked:verify-failed
bug
deferred
feature-request
in-progress
needs-decompose
needs-triage
quickfix
security/medium
triage-approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Mark/Kimi_Agent_Brother_10_Digitizing_App#153
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Finding
The app has zero React error boundaries. A runtime exception in any component (a bad image, a parse failure, a math overflow, a missing null check) results in a blank white screen with no recovery affordance. The user must refresh the page, losing any in-memory state.
Verified:
grep -rn 'ErrorBoundary\|componentDidCatch\|getDerivedStateFromError' src/returns no matches.The closest thing to a defensive layer is the
useWorkspacereducer'stry/catchblocks around dispatch handlers, but those only protect the dispatch path, not the render path. ATypeErrorthrown duringCanvasArea's render orEditorCanvas's event handler will crash the entire React tree.Where
src/directory: noErrorBoundarycomponent, nocomponentDidCatchimplementation, nogetDerivedStateFromErrorstatic methodsrc/App.tsx—<Routes>is not wrapped in any boundarysrc/pages/Help.tsx:392("Search topics, stitch types, error messages...") — the help page advertises "error messages" as searchable content but the app has no structured error-message system to searchSuggested fix shape
Three layers:
Root-level
ErrorBoundarywrapping<Routes>inApp.tsx. Catches any uncaught render error from any page. Shows a "Something went wrong — Reload" UI with the error message and a stack trace (in dev) or a generic message (in prod). Reload button callswindow.location.reload().Section-level boundaries in each page that has complex state: Workspace, Editor, Gallery. A render error in the Gallery's
DesignThumbnailshouldn't take down the user's edit in the Editor (if they were using modals across pages) or vice versa. The Editor and Workspace are each their own routes, so the root boundary is enough for that. But the Settings modal and the Export dialog are portaled — if they throw, the parent should catch.Error state inside
useWorkspacefor the digitize/export flow. The reducer's existingaddToastcalls cover user-facing errors. The error boundary covers the unexpected.Severity
High. A single render error in any component takes the entire app down. The user has no recourse but a hard refresh. For a product that handles user-uploaded files (which can be malformed, oversized, corrupted), this is a real availability issue.
Discovered by
Gap analysis (2026-10-02) of app resilience.
Related
Fixed in commit
85d342fon origin/main.What landed:
src/components/ErrorBoundary.tsx— class component withcomponentDidCatch+getDerivedStateFromError. Default fallback shows the scope name, the error message, and Try-again / Reload buttons; dev mode also shows the stack trace.src/App.tsxnow wraps<Routes>in a rootErrorBoundary scope="App"plus a per-route scoped boundary (Home / Workspace / Editor / Gallery / Help). A crash in one page leaves the navbar/footer usable so the user can navigate away.fallbackprop accepts a customReact.ComponentType<{error, reset}>for callers that want to override the default panel.componentDidCatchlogs toconsole.errorwith a[ErrorBoundary:<scope>]tag — drop-in hook for a future Sentry / log layer.Verification: 8 new tests cover the default fallback, custom fallback, recovery via reset, re-throw after reset, and nested-boundary isolation. Full suite: 642/642 pass. eslint clean, tsc clean, vite build clean.
Closes the gap: a render error in any component now produces a scoped fallback UI instead of a blank white screen with no recovery affordance.