SfSkills Domains Docs GitHub

Home / lwc / lwc-error-boundaries

lwc-error-boundaries

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.

Open the skill folder on GitHub


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:

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.

AskWhy it mattersWhat 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 labelWhether 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 problemThe 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.

  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

Producererror.body isGuide line
UI API read (getRecord, related lists)array of objectsdata-error L6568
UI API write (createRecord, updateRecord)object, often with object- and field-level errorsdata-error L6569
Apex read and writeobjectdata-error L6570
Network, such as offlineobjectdata-error L6571
errorCallback(error, stack)no body at all — a native Error, and stack is a stringcreate-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

FailureWhere it surfaces
Throw in a descendant's lifecycle hookAncestor errorCallback (L4158)
Throw in a descendant's template-declared handlerAncestor errorCallback (L4158)
Throw in a programmatically attached handlerNowhere — local try/catch (L4167)
Rejected promise from imperative ApexNowhere — .catch / try/await (L6513, L6524)
Wire adapter failureThe wired property's error member (L6542)
Nothing catches it at allThe 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

Worked Examples (see references/examples.md)

Common Gotchas (see references/gotchas.md)

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

Official Sources Used

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

Reference Files

FileRead it when
references/code-examples.mdYou 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.mdYou want the worked scenarios and the four fixture payloads to test the normaliser against
references/gotchas.mdSomething behaves unexpectedly — the array body, the silent retry, the invisible toast, the boundary that cannot query its child
references/well-architected.mdYou are justifying boundary placement, normalisation or telemetry in a design review, and need the source list
references/llm-anti-patterns.mdYou are reviewing generated boundary code, or about to generate some

Package files