Skip to content
Back to skills

Lwc Error Boundaries

ASecurity

Isolate component errors so one failure does not blank an entire page using errorCallback and graceful fallbacks. NOT for server-side Apex exception design — use apex/exception-handling. Also covers: normalising error.body across UI API read, UI API write, Apex and network shapes; fallback UI; retry by remount; telemetry hand-off from the boundary.

  • 15 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 6, 2026
ai-agentsjavascriptpythongojavashellexpressspringtestingapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 7 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add PranavNagrecha/AwesomeSalesforceSkills --skill lwc-error-boundaries --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Lwc Error Boundaries?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Lwc Error Boundaries
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/pranavnagrecha-lwc-error-boundaries/badge)](https://www.skillsdirectory.com/skills/pranavnagrecha-lwc-error-boundaries)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: lwc-error-boundaries
description: "Isolate component errors so one failure does not blank an entire page using errorCallback and graceful fallbacks. NOT for server-side Apex exception design — use apex/exception-handling. Also covers: normalising error.body across UI API read, UI API write, Apex and network shapes; fallback UI; retry by remount; telemetry hand-off from the boundary."
category: lwc
salesforce-version: "Spring '25+"
well-architected-pillars:
  - Reliability
  - User Experience
triggers:
  - "lwc errorcallback"
  - "lwc component blank page"
  - "error boundary lwc"
  - "lwc graceful fallback"
  - "stop one broken LWC tile from blanking the whole dashboard page"
  - "normalise wire and Apex error shapes into one message list in LWC"
  - "read error.body when it is sometimes an array and sometimes an object"
  - "add a retry button that remounts a failed LWC child component"
  - "log LWC client errors to a custom object from errorCallback"
  - "write a Jest test that makes a child LWC throw into errorCallback"
  - "fallback UI renders but nothing is logged for the LWC failure"
  - "show an Apex AuraHandledException message in an LWC without leaking the class name"
tags:
  - error-handling
  - lwc
  - errorcallback
  - error-normalisation
  - fallback-ui
  - telemetry
inputs:
  - "parent component"
  - "child components that may error"
  - "which error producers the subtree uses — UI API wire, imperative Apex, network"
  - "where client errors are recorded today (custom object, external logger, or nowhere)"
outputs:
  - "wrapper boundary component + fallback UI"
  - "shared error-normalisation module reducing the four documented body shapes to one structure"
  - "Jest tests per normaliser branch plus a boundary test with a throwing child"
  - "static-check findings from scripts/check_lwc_error_boundaries.py"
dependencies: []
version: 1.2.0
author: Pranav Nagrecha
updated: 2026-09-05
---

# LWC Error Boundaries

`errorCallback(error, stack)` on any ancestor "captures errors in all the descendent
components in its tree" — errors "that occur in lifecycle hooks or during an event handler
declared in an HTML template" (`lwc_guide create-lifecycle-hooks-error L4158`). Wrapping
each widget in a reusable boundary keeps one failure from removing the whole page.

This skill covers the boundary *component*: where to place it, what its fallback may safely
depend on, how to reduce the four documented error shapes to one structure the fallback and
the telemetry record can both use, how to offer a retry that actually re-mounts the child,
and how to prove all of it with Jest. The hook's own contract and its position in the
lifecycle belong to `lwc/lifecycle-hooks`.

## Before Starting

Check for `salesforce-context.md` in the project root. If present, read it first —
particularly whether any of these components land on an Experience Cloud site, which
changes which toast module is legal (`references/gotchas.md`, Gotcha 10).

Gather if not available:

- Which subtrees are independently useful, and which would leave the page pointless if they
  vanished.
- Which error producers each subtree uses: UI API wire adapters, imperative Apex, plain
  `fetch`, or a mix. Each has a different `error.body` shape.
- Where client errors are recorded today, if anywhere.
- Whether users can meaningfully retry, or whether the failure is deterministic.

## Questions to Ask Before Configuring

Ask these before writing the wrapper. An assistant that skips them produces a grey box that
looks correct in review and reports nothing in production.

| Ask | Why it matters | What a good answer adds |
|---|---|---|
| "If this subtree disappears, can the user still do something useful on this page?" | The guide leaves placement to you — "You can wrap the entire app, or every individual component" (L4160) — but the throwing subtree is unmounted and removed from the DOM (L4162) | The boundary count and their exact positions in the markup |
| "Which of these components read from a UI API wire, and which call Apex imperatively?" | UI API **reads** return `error.body` as an array; UI API writes, Apex and network errors return an object (L6568–L6571) | Which normaliser branches are actually exercised, and which fixtures the Jest suite needs |
| "Does the failing operation have any chance of succeeding on a second attempt?" | A Retry button on a deterministic failure is a loop with a friendly label | Whether the fallback gets a Retry button, and what `maxRetries` should be |
| "Where do client-side errors go today — a custom object, an external logger, or nowhere?" | A silent boundary removes the user's only reason to report the problem while removing none of the problem | The telemetry target, the payload fields, and who reads it |
| "Is this component ever placed on an Experience Cloud page?" | `lightning/platformShowToastEvent` "isn't supported in environments like LWR sites for Experience Cloud or standalone apps" (L4773) | The toast module choice, and whether `lightningCommunity__Default` belongs in the `.js-meta.xml` |
| "Does the Apex behind this throw `AuraHandledException`, or does it throw uncaught?" | An uncaught Apex exception surfaces the Apex class name in `body.stackTrace` at the client (L7518); `AuraHandledException` omits it (L7521) | Whether the fallback may show the message at all, and a work item for `apex/exception-handling` |
| "Are any event handlers in this subtree attached in JavaScript rather than in the template?" | Errors from programmatically assigned handlers are not caught by `errorCallback` (L4167) | The list of handlers that need their own `try/catch`, since the boundary will never see them |

What a proper boundary adds over just implementing `errorCallback`: the failure stops at a
subtree the user can afford to lose, the message shown comes from the right level of the
error object regardless of which API produced it, the failure is recorded before the fallback
renders, and a recoverable failure gets one bounded retry instead of a page reload.

## Recommended Workflow

1. **Place the boundaries.** Mark each independently useful subtree in the parent template.
   Wrap each one — not the page — using the placement test from the Questions table above.
   Record the count; it is the number of `boundary-name` values you will see in telemetry.
2. **Deploy the normaliser first.** Copy the `errorUtils` module from
   `references/code-examples.md` (Bundle 1). It is the only place in the bundle set that
   knows `error.body` is an array for a UI API read and an object for everything else.
   Every branch in it carries its guide line; keep the citations when you copy it.
3. **Build the boundary.** Copy `errorBoundary` (Bundle 2): `errorCallback` → normalise →
   `hasError` → fallback with `lwc:if` / `lwc:else` → optional Retry → telemetry inside its
   own `try/catch`. Use `templates/lwc/component-skeleton/` for the bundle shell. Do not add
   a wire adapter or an imperative call to the boundary; it must have nothing of its own that
   can throw.
4. **Handle what the boundary cannot see, in the child.** Copy the `revenueTile` pattern
   (Bundle 3) for wire `error` branches, imperative `catch` blocks and programmatic handlers,
   routing all three into the same normaliser. Cross-check against
   `templates/lwc/patterns/imperativeApexPattern.js` and
   `templates/lwc/patterns/wireServicePattern.js`.
5. **Run the checker.**
   `python3 skills/lwc/lwc-error-boundaries/scripts/check_lwc_error_boundaries.py --manifest-dir force-app/main/default/lwc`
   Fix every ERROR (EB001 swallowed `errorCallback`, EB002 rethrow). Add `--strict` in CI to
   fail on the WARN rules too (EB003 unguarded `error.body.message`, EB004 console-only
   error branch, EB005 `platformShowToastEvent` on an Experience Builder target).
6. **Test both halves.** Port the Jest suites from `references/code-examples.md`: one case
   per normaliser branch with the fixture payloads from `references/examples.md`, and the
   boundary suite with a throwing child, the telemetry hand-off, the no-rethrow assertion
   and the retry re-mount.
7. **Verify in the org.** Work the seven-row verification table at the end of
   `references/code-examples.md` — deploy, force a failure, confirm one tile degrades, and
   confirm a telemetry row exists for it.

## The four error shapes, once

| Producer | `error.body` is | Guide line |
|---|---|---|
| UI API read (`getRecord`, related lists) | array of objects | `data-error L6568` |
| UI API write (`createRecord`, `updateRecord`) | object, often with object- and field-level errors | `data-error L6569` |
| Apex read and write | object | `data-error L6570` |
| Network, such as offline | object | `data-error L6571` |
| `errorCallback(error, stack)` | no `body` at all — a native `Error`, and `stack` is a string | `create-lifecycle-hooks-error L4164` |

The wrapper around the first four is a `FetchResponse`: `body`, `ok` (always false, status
400–599), `status`, `statusText` (`data-error L6545–L6551`). Before a wire fires, `data` and
`error` are both undefined — not an error state (`L6572`).

## Division of labour

| Failure | Where it surfaces |
|---|---|
| Throw in a descendant's lifecycle hook | Ancestor `errorCallback` (`L4158`) |
| Throw in a descendant's template-declared handler | Ancestor `errorCallback` (`L4158`) |
| Throw in a programmatically attached handler | Nowhere — local `try/catch` (`L4167`) |
| Rejected promise from imperative Apex | Nowhere — `.catch` / `try/await` (`L6513`, `L6524`) |
| Wire adapter failure | The wired property's `error` member (`L6542`) |
| Nothing catches it at all | The app shell's "A Component Error has occurred!" modal (`L7732`) |

## Adoption Signals

Dashboards with multiple independent widgets; record home pages with many components; any
page where a single tile calls Apex and a second tile is the reason users opened the page.

## Key Considerations

- The boundary owns no business logic, no wire adapter and no imperative call — anything it
  owns is a way for the error handler itself to fail.
- The fallback must not depend on data that may be the reason the boundary fired.
- `lwc:if` takes a property or a getter, never an expression: `lwc:if={!hasError}` is not
  supported (`reference-directives L19510`). Use `lwc:else`.
- Cap retries. A deterministic failure will happily re-mount forever.
- Keep `body.stackTrace` and the `stack` string in telemetry, never in the rendered message.

## Worked Examples (see `references/examples.md`)

- *Dashboard tile isolation* — six-tile sales dashboard, one boundary per tile
- *The failures the boundary does not catch* — wire error, promise rejection, programmatic handler
- *The four error payloads, as fixtures* — one JSON block the Jest suite asserts against

## Common Gotchas (see `references/gotchas.md`)

- **`error.body` is an array for UI API reads** — `error.body.message` returns `undefined`.
- **A legal app-level boundary still costs the whole app** — the unmount takes everything below it.
- **A fallback with dependencies** — fails inside the failure handler, with nothing above to catch it.
- **Toggling the retry flag twice in one block** — both writes land in the same microtask, so nothing happens.

## Top LLM Anti-Patterns (full list in `references/llm-anti-patterns.md`)

- Reading `error.body.message` without an `Array.isArray` guard
- Rethrowing from `errorCallback` to "let something upstream handle it"
- Catching silently, so production failures are invisible
- A fallback that renders the raw error object, Apex class name and all

## Official Sources Used

Full list with the claim each supports in `references/well-architected.md` §
Official Sources Used.

- errorCallback() — https://developer.salesforce.com/docs/platform/lwc/guide/create-lifecycle-hooks-error.html
- Handle Errors in Lightning Data Service — https://developer.salesforce.com/docs/platform/lwc/guide/data-error.html
- Work with Errors — https://developer.salesforce.com/docs/platform/lwc/guide/data-error-types.html
- Handle Errors from Apex — https://developer.salesforce.com/docs/platform/lwc/guide/apex-error-handling.html
- Toast Notifications — https://developer.salesforce.com/docs/platform/lwc/guide/use-toast.html

## Reference Files

| File | Read it when |
|---|---|
| `references/code-examples.md` | You are writing the components: the `errorUtils` module, the `errorBoundary` bundle, the `revenueTile` bundle, `.js-meta.xml`, `package.xml`, deploy order, both Jest suites, and the verification table |
| `references/examples.md` | You want the worked scenarios and the four fixture payloads to test the normaliser against |
| `references/gotchas.md` | Something behaves unexpectedly — the array body, the silent retry, the invisible toast, the boundary that cannot query its child |
| `references/well-architected.md` | You are justifying boundary placement, normalisation or telemetry in a design review, and need the source list |
| `references/llm-anti-patterns.md` | You are reviewing generated boundary code, or about to generate some |

## Related Skills

- **lwc/lifecycle-hooks**: owns the `errorCallback` contract, hook ordering, and the minimal boundary example. Read it first; this skill assumes it.
- **lwc/wire-service-patterns**: the wire `{ data, error }` contract and refresh behaviour behind the wire branch here.
- **lwc/lwc-toast-and-notifications**: toast copy, variants, containers, and which module works in which container.
- **lwc/common-lwc-runtime-errors**: the catalogue of runtime error messages you will be normalising.
- **lwc/lwc-testing**: broader Jest patterns; this skill carries only the boundary and normaliser suites.
- **apex/exception-handling**: the server side — throwing `AuraHandledException` so the client never sees an Apex class name.
- **apex/debug-and-logging**: where the telemetry payload from `errorCallback` should land, and how to read it back.

Files in this skill

  • SKILL.md3 KB
  • references/examples.md6.3 KB
  • references/gotchas.md591 B
  • references/llm-anti-patterns.md7.4 KB
  • references/well-architected.md2.9 KB
  • scripts/check_lwc_error_boundaries.py918 B
  • templates/lwc-error-boundaries-template.md542 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…