# Partial page failures do not crash the entire page

- **Pattern:** `ab-002301` (`saas-error-handling.graceful-degradation.partial-failures-dont-crash`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002301
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002301)

## 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.

```tsx
// 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.tsx` and `error.tsx` files 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:

  ```tsx
  // 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
- iso-25010 reliability.fault-tolerance

Taxons: error-resilience

HTML version: https://auditbuffet.com/patterns/ab-002301
