[GH-ISSUE #7391] [Maintenance] Add scoped ErrorBoundaries to isolate feature-level crashes #117451

Open
opened 2026-06-11 12:37:28 -05:00 by GiteaMirror · 6 comments
Owner

Originally created by @MatissJanis on GitHub (Apr 5, 2026).
Original GitHub issue: https://github.com/actualbudget/actual/issues/7391

Summary

When a component throws a rendering error, the entire app crashes to the "Fatal Error" screen. This happens because we only have two top-level ErrorBoundary wrappers (in App.tsx) and one feature-scoped boundary (in GetCardData.tsx for report charts).

Adding scoped ErrorBoundary components around major features would contain failures to the affected area instead of taking down the whole app.

Problem

A single rendering error in any feature — budget, accounts, transactions, reports, schedules, rules — crashes the entire application. Users lose all context and must restart.

This is a recurring pattern. Across open and closed issues, there are 70+ reports of fatal crashes caused by component-level errors that could have been contained:

Currently open:

  • #7273 — Fatal Error after reports .json file import
  • #7285 — Fatal Error viewing ledgers with recurring transactions ending in the past
  • #7098 — Custom Report bars crash
  • #7108 — Calendar widget crashes on mobile
  • #6073 — Hide reconciled transactions crashes app
  • #7358 — Dollar sign in Schedule Name crashes app
  • #6317 — Backslash in rule notes causes crash
  • #5351 — API Unhandled Rejection

Recurring crash patterns from closed issues:

The fix for #4703 (adding an ErrorBoundary around modals) shows this approach works. We should apply it systematically.

Proposed approach

Wrap major feature areas with ErrorBoundary from react-error-boundary (already a dependency at v6.0.3). Each boundary should:

  1. Catch and contain the error to that feature area
  2. Show a contextual fallback (e.g., "This report couldn't be loaded" instead of a full-app fatal screen)
  3. Offer a retry via resetErrorBoundary where it makes sense

A shared FeatureErrorFallback component now lives at packages/desktop-client/src/components/FeatureErrorFallback.tsx and is used by all completed boundaries — please reuse it for consistency.

Suggested areas to add boundaries

Desktop / shared

Status Area Entry point(s) Related crashes
Budget table BudgetTable / BudgetPageHeader (wrapped in DynamicBudgetTable.tsx) #5969, #6073
Account ledger Account component (accounts/Account.tsx) #7285, #3263, #595
Transaction list TransactionList (transactions/TransactionList.tsx) #5304, #1021
Reports (individual) Each full-page report view — NetWorth.tsx, CashFlow.tsx, Spending.tsx, CustomReport.tsx, Calendar.tsx, BalanceForecast.tsx, Sankey.tsx, Summary.tsx, AgeOfMoney.tsx, Crossover.tsx, Formula.tsx, BudgetAnalysis.tsx #7273, #7098, #5814, #5764, #6221
Schedules Schedules page (schedules/index.tsx) #7358, #3989, #3954
Rules Rules page (ManageRulesPage.tsx, plus route-level wrapping in FinancesApp.tsx) #6317, #6885, #4013, #2180
Sidebar Sidebar component (sidebar/Sidebar.tsx) #5467
Dashboard widgets Individual widget cards (per-card boundary in reports/Overview.tsx) #7108, #6313
Individual modals Each modal content (boundary in common/Modal.tsx) #4703 pattern

Mobile

None of the mobile entry points currently have feature-scoped boundaries — grep -r "ErrorBoundary" packages/desktop-client/src/components/mobile/ returns zero hits. Several of the linked crashes (#7108, #6650, #6467, #5692, #4204, #3263) are mobile-specific, so each of these pages should mirror its desktop counterpart.

Status Area Entry point(s)
🔴 Mobile budget mobile/budget/BudgetPage.tsx (wrapping BudgetTable)
🔴 Mobile category transactions mobile/budget/CategoryPage.tsx / CategoryTransactions.tsx
🔴 Mobile accounts list mobile/accounts/AccountsPage.tsx
🔴 Mobile account ledger mobile/accounts/AccountPage.tsx (and the *AccountTransactions.tsx variants)
🔴 Mobile transaction list mobile/transactions/TransactionList.tsx / TransactionListWithBalances.tsx
🔴 Mobile transaction edit mobile/transactions/TransactionEdit.tsx
🔴 Mobile schedules mobile/schedules/MobileSchedulesPage.tsx (and MobileScheduleEditPage.tsx)
🔴 Mobile rules mobile/rules/MobileRulesPage.tsx (and MobileRuleEditPage.tsx)
🔴 Mobile payees mobile/payees/MobilePayeesPage.tsx (and MobilePayeeEditPage.tsx)
🔴 Mobile bank sync mobile/banksync/MobileBankSyncPage.tsx (and MobileBankSyncAccountEditPage.tsx)

Implementation notes

  • react-error-boundary v6 is already a dependency — no new packages needed
  • FatalError.tsx has good patterns for error display that can be adapted
  • The shared FeatureErrorFallback component keeps UX consistent across boundaries
  • Each boundary can use onError to log to the existing notification system

This is a good first contribution

Each remaining boundary is a small, self-contained change — pick a report view or a mobile page from the tables above. The pattern is straightforward:

import { ErrorBoundary } from 'react-error-boundary';
import { FeatureErrorFallback } from '#components/FeatureErrorFallback';

<ErrorBoundary FallbackComponent={FeatureErrorFallback}>
  <YourFeatureComponent />
</ErrorBoundary>
Originally created by @MatissJanis on GitHub (Apr 5, 2026). Original GitHub issue: https://github.com/actualbudget/actual/issues/7391 ## Summary When a component throws a rendering error, the entire app crashes to the "Fatal Error" screen. This happens because we only have two top-level `ErrorBoundary` wrappers (in `App.tsx`) and one feature-scoped boundary (in `GetCardData.tsx` for report charts). Adding scoped `ErrorBoundary` components around major features would contain failures to the affected area instead of taking down the whole app. ## Problem A single rendering error in any feature — budget, accounts, transactions, reports, schedules, rules — crashes the entire application. Users lose all context and must restart. This is a recurring pattern. Across open and closed issues, there are **70+ reports** of fatal crashes caused by component-level errors that could have been contained: **Currently open:** - #7273 — Fatal Error after reports .json file import - #7285 — Fatal Error viewing ledgers with recurring transactions ending in the past - #7098 — Custom Report bars crash - #7108 — Calendar widget crashes on mobile - #6073 — Hide reconciled transactions crashes app - #7358 — Dollar sign in Schedule Name crashes app - #6317 — Backslash in rule notes causes crash - #5351 — API Unhandled Rejection **Recurring crash patterns from closed issues:** - **Filter condition changes** (is ↔ one-of switching) — #6446, #6479, #6325, #6452, #6590, #5476, #5347 - **Reports/charts rendering** — #6221, #5814, #5729, #5764, #6406, #1792, #4307 - **Rules and schedules** — #6885, #6422, #6160, #5258, #4013, #3989, #3954, #2180 - **Date field handling** — #5304, #5094, #5969 - **Mobile/browser-specific** — #6650, #6467, #5692, #4204, #3263 - **Modals** — #4703 (fixed by adding an ErrorBoundary — proving the pattern works) The fix for #4703 (adding an ErrorBoundary around modals) shows this approach works. We should apply it systematically. ## Proposed approach Wrap major feature areas with `ErrorBoundary` from `react-error-boundary` (already a dependency at v6.0.3). Each boundary should: 1. **Catch and contain** the error to that feature area 2. **Show a contextual fallback** (e.g., "This report couldn't be loaded" instead of a full-app fatal screen) 3. **Offer a retry** via `resetErrorBoundary` where it makes sense A shared `FeatureErrorFallback` component now lives at `packages/desktop-client/src/components/FeatureErrorFallback.tsx` and is used by all completed boundaries — please reuse it for consistency. ### Suggested areas to add boundaries #### Desktop / shared | Status | Area | Entry point(s) | Related crashes | |--------|------|----------------|-----------------| | ✅ | Budget table | `BudgetTable` / `BudgetPageHeader` (wrapped in `DynamicBudgetTable.tsx`) | #5969, #6073 | | ✅ | Account ledger | `Account` component (`accounts/Account.tsx`) | #7285, #3263, #595 | | ✅ | Transaction list | `TransactionList` (`transactions/TransactionList.tsx`) | #5304, #1021 | | ✅ | Reports (individual) | Each full-page report view — `NetWorth.tsx`, `CashFlow.tsx`, `Spending.tsx`, `CustomReport.tsx`, `Calendar.tsx`, `BalanceForecast.tsx`, `Sankey.tsx`, `Summary.tsx`, `AgeOfMoney.tsx`, `Crossover.tsx`, `Formula.tsx`, `BudgetAnalysis.tsx` | #7273, #7098, #5814, #5764, #6221 | | ✅ | Schedules | `Schedules` page (`schedules/index.tsx`) | #7358, #3989, #3954 | | ✅ | Rules | `Rules` page (`ManageRulesPage.tsx`, plus route-level wrapping in `FinancesApp.tsx`) | #6317, #6885, #4013, #2180 | | ✅ | Sidebar | `Sidebar` component (`sidebar/Sidebar.tsx`) | #5467 | | ✅ | Dashboard widgets | Individual widget cards (per-card boundary in `reports/Overview.tsx`) | #7108, #6313 | | ✅ | Individual modals | Each modal content (boundary in `common/Modal.tsx`) | #4703 pattern | #### Mobile None of the mobile entry points currently have feature-scoped boundaries — `grep -r "ErrorBoundary" packages/desktop-client/src/components/mobile/` returns zero hits. Several of the linked crashes (#7108, #6650, #6467, #5692, #4204, #3263) are mobile-specific, so each of these pages should mirror its desktop counterpart. | Status | Area | Entry point(s) | |--------|------|----------------| | 🔴 | Mobile budget | `mobile/budget/BudgetPage.tsx` (wrapping `BudgetTable`) | | 🔴 | Mobile category transactions | `mobile/budget/CategoryPage.tsx` / `CategoryTransactions.tsx` | | 🔴 | Mobile accounts list | `mobile/accounts/AccountsPage.tsx` | | 🔴 | Mobile account ledger | `mobile/accounts/AccountPage.tsx` (and the `*AccountTransactions.tsx` variants) | | 🔴 | Mobile transaction list | `mobile/transactions/TransactionList.tsx` / `TransactionListWithBalances.tsx` | | 🔴 | Mobile transaction edit | `mobile/transactions/TransactionEdit.tsx` | | 🔴 | Mobile schedules | `mobile/schedules/MobileSchedulesPage.tsx` (and `MobileScheduleEditPage.tsx`) | | 🔴 | Mobile rules | `mobile/rules/MobileRulesPage.tsx` (and `MobileRuleEditPage.tsx`) | | 🔴 | Mobile payees | `mobile/payees/MobilePayeesPage.tsx` (and `MobilePayeeEditPage.tsx`) | | 🔴 | Mobile bank sync | `mobile/banksync/MobileBankSyncPage.tsx` (and `MobileBankSyncAccountEditPage.tsx`) | ### Implementation notes - `react-error-boundary` v6 is already a dependency — no new packages needed - `FatalError.tsx` has good patterns for error display that can be adapted - The shared `FeatureErrorFallback` component keeps UX consistent across boundaries - Each boundary can use `onError` to log to the existing notification system ## This is a good first contribution Each remaining boundary is a small, self-contained change — pick a report view or a mobile page from the tables above. The pattern is straightforward: ```tsx import { ErrorBoundary } from 'react-error-boundary'; import { FeatureErrorFallback } from '#components/FeatureErrorFallback'; <ErrorBoundary FallbackComponent={FeatureErrorFallback}> <YourFeatureComponent /> </ErrorBoundary> ```
GiteaMirror added the help wantedtech debtmaintenancegood first issue labels 2026-06-11 12:37:33 -05:00
Author
Owner

@github-actions[bot] commented on GitHub (Apr 5, 2026):

Thanks for sharing your idea!

This repository uses a voting-based system for feature requests. While enhancement issues are automatically closed, we still welcome feature requests! The voting system helps us gauge community interest in potential features. We also encourage community contributions for any feature requests marked as needing votes (just post a comment first so we can help guide you toward a successful contribution).

The enhancement backlog can be found here: https://github.com/actualbudget/actual/issues?q=label%3A%22needs+votes%22+sort%3Areactions-%2B1-desc+

Don't forget to upvote the top comment with 👍!

<!-- gh-comment-id:4189503724 --> @github-actions[bot] commented on GitHub (Apr 5, 2026): :sparkles: Thanks for sharing your idea! :sparkles: This repository uses a voting-based system for feature requests. While enhancement issues are automatically closed, we still welcome feature requests! The voting system helps us gauge community interest in potential features. We also encourage community contributions for any feature requests marked as needing votes (just post a comment first so we can help guide you toward a successful contribution). The enhancement backlog can be found here: https://github.com/actualbudget/actual/issues?q=label%3A%22needs+votes%22+sort%3Areactions-%2B1-desc+ Don't forget to upvote the top comment with 👍! <!-- feature-auto-close-comment -->
Author
Owner

@MatissJanis commented on GitHub (May 15, 2026):

Looks like most desktop error boundaries have been added! 🎉

Updated the list in issue description. We should also expand the ErrorBoundaries to mobile. Anyone: feel free to take this on!

<!-- gh-comment-id:4461763860 --> @MatissJanis commented on GitHub (May 15, 2026): Looks like most desktop error boundaries have been added! 🎉 Updated the list in issue description. We should also expand the ErrorBoundaries to mobile. Anyone: feel free to take this on!
Author
Owner

@MatissJanis commented on GitHub (Jun 5, 2026):

ErrorBoundaries for desktop app are all done. Big thanks for the help!

Missing: ErrorBoundaries for mobile.

<!-- gh-comment-id:4635468170 --> @MatissJanis commented on GitHub (Jun 5, 2026): ErrorBoundaries for desktop app are all done. Big thanks for the help! Missing: ErrorBoundaries for mobile.
Author
Owner

@BasilTh commented on GitHub (Jun 11, 2026):

Hello @MatissJanis I'm Basil Thomas. I'm taking CodePath's AI301 Open Source Capstone, a 10 week program where we contribute to real open source projects, and I'd like to take on the mobile half of this issue, per your note that it's open for anyone.

Scope I'm proposing, based on the table in the description and the review feedback on #8094:

  • Mobile page level boundaries only - all 10 areas in the mobile table (budget, category transactions, accounts list/ledger, transaction list/edit, schedules, rules, payees, bank sync), wrapping the listed entry points
  • Reuse FeatureErrorFallback for consistency, with onError wired to the notification system like the merged desktop boundaries
  • No report-route wrapping (already covered in the router since #7658), no unrelated changes
  • For verification, I'll reproduce the linked mobile-specific crashes (#7108, #6650, #6467, #5692, #4204, #3263) where still reproducible and confirm the boundary contains them instead of the fatal screen

One question before I start: would you prefer a single PR covering all mobile pages, or grouped smaller PRs (e.g., budget + accounts + transactions first, then schedules/rules/payees/bank sync)? Happy to go either way.

<!-- gh-comment-id:4676000748 --> @BasilTh commented on GitHub (Jun 11, 2026): Hello @MatissJanis I'm Basil Thomas. I'm taking CodePath's AI301 Open Source Capstone, a 10 week program where we contribute to real open source projects, and I'd like to take on the mobile half of this issue, per your note that it's open for anyone. Scope I'm proposing, based on the table in the description and the review feedback on #8094: - Mobile page level boundaries only - all 10 areas in the mobile table (budget, category transactions, accounts list/ledger, transaction list/edit, schedules, rules, payees, bank sync), wrapping the listed entry points - Reuse FeatureErrorFallback for consistency, with onError wired to the notification system like the merged desktop boundaries - No report-route wrapping (already covered in the router since #7658), no unrelated changes - For verification, I'll reproduce the linked mobile-specific crashes (#7108, #6650, #6467, #5692, #4204, #3263) where still reproducible and confirm the boundary contains them instead of the fatal screen One question before I start: would you prefer a single PR covering all mobile pages, or grouped smaller PRs (e.g., budget + accounts + transactions first, then schedules/rules/payees/bank sync)? Happy to go either way.
Author
Owner

@MatissJanis commented on GitHub (Jun 11, 2026):

@BasilTh one PR is fine. Just please read the AI usage policy page in our docs.

<!-- gh-comment-id:4678166336 --> @MatissJanis commented on GitHub (Jun 11, 2026): @BasilTh one PR is fine. Just please read the AI usage policy page in our docs.
Author
Owner

@BasilTh commented on GitHub (Jun 11, 2026):

Thank you @MatissJanis, I'll look into the AI usage policy and get to work.

<!-- gh-comment-id:4678300884 --> @BasilTh commented on GitHub (Jun 11, 2026): Thank you @MatissJanis, I'll look into the AI usage policy and get to work.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/actual#117451