Partial page failures do not crash the entire page
Why it matters
A dashboard that fetches user profile, billing status, and activity feed in parallel should degrade gracefully when only one source fails — the other two sections are still useful. CWE-755 (Improper Handling of Exceptional Conditions) applies when a single data source failure crashes an entire page that could have partially rendered. ISO 25010 reliability.fault-tolerance requires that failures in one subsystem do not propagate to others. Practically, a single top-level error boundary means a transient billing API timeout takes down the entire dashboard, blocking users from accessing unrelated functionality they need.
Severity rationale
High because a single top-level boundary lets any one component failure crash an entire multi-source page, blocking access to all other independent sections.
Remediation
Wrap each independent data-fetching section in its own ErrorBoundary and Suspense pair so failures are contained to the section that failed.
// dashboard/page.tsx
<div className="grid grid-cols-3 gap-4">
<ErrorBoundary fallback={<WidgetError name="Profile" />}>
<Suspense fallback={<Skeleton />}><ProfileWidget /></Suspense>
</ErrorBoundary>
<ErrorBoundary fallback={<WidgetError name="Activity" />}>
<Suspense fallback={<Skeleton />}><ActivityFeed /></Suspense>
</ErrorBoundary>
<ErrorBoundary fallback={<WidgetError name="Billing" />}>
<Suspense fallback={<Skeleton />}><BillingSummary /></Suspense>
</ErrorBoundary>
</div>
In Next.js App Router, add error.tsx and loading.tsx at sub-route segments for parallel routes to get this isolation automatically at the route level.
Detection
-
ID:
partial-failures-dont-crash -
Severity:
high -
What to look for: Examine page components that render multiple independent data-fetching sections or widgets. Look for: (1) whether each data-dependent section has its own error boundary rather than sharing a single top-level boundary; (2) whether React Suspense boundaries are placed at a granular level (around individual sections rather than the whole page); (3) whether a failure in one section (e.g., an activity feed widget) prevents the rest of the page from loading. In Next.js App Router, look for
loading.tsxanderror.tsxfiles at sub-route levels, not just the root. Check dashboard-style pages and pages with multiple API calls. -
Pass criteria: Count all pages with multiple independent data sources and check each for section-level error isolation. Pass if the application has component-level or section-level error boundaries on pages with multiple independent data sources, such that a failure in 1 section allows the rest of the page to render. Report the count: "X of Y multi-source pages have section-level error boundaries."
-
Fail criteria: Fail if the entire page component is wrapped in a single top-level boundary with no granular boundaries, and a failure in any section crashes the full page. At least 1 page with 2 or more data sources must have section-level error isolation. Fail if there are no component-level boundaries anywhere in the application (only page-level or route-level).
-
Skip (N/A) when: The application is a simple single-purpose page with only one data source per view. Signal: all pages fetch a single data set; no dashboard-style layouts with multiple independent widgets or sections.
-
Detail on fail:
"Dashboard page fetches user profile, recent activity, and billing status in parallel; single error boundary at the route level; any individual failure crashes the entire dashboard". Max 500 chars. -
Remediation: A single top-level error boundary is better than nothing, but it's a blunt instrument. When possible, isolate failures to the section that failed.
Wrap independent sections in their own boundaries:
// dashboard/page.tsx <div className="grid grid-cols-3 gap-4"> <ErrorBoundary fallback={<WidgetError name="Profile" />}> <Suspense fallback={<Skeleton />}> <ProfileWidget /> </Suspense> </ErrorBoundary> <ErrorBoundary fallback={<WidgetError name="Activity" />}> <Suspense fallback={<Skeleton />}> <ActivityFeed /> </Suspense> </ErrorBoundary> <ErrorBoundary fallback={<WidgetError name="Billing" />}> <Suspense fallback={<Skeleton />}> <BillingSummary /> </Suspense> </ErrorBoundary> </div>This way, if the activity feed fails, the profile and billing widgets still render.
External references
- cwe · CWE-755 — Improper Handling of Exceptional Conditions
- iso-25010:2011 · reliability.fault-tolerance
Taxons
History
- 2026-04-18·v1.0.0·Initial import from saas-error-handling·automated