Error Meshing: Deep Dive #61
Principal React Architect
Error handling scales when it becomes a mesh, not a single “boundary.” A mesh is a routing artifact: it connects failures to recovery paths and keeps the route story stable while behavior changes underneath.
This post follows the plan’s “Error Meshing” topic focus and global requirements:
- The App Router Mindset
- Core Concepts (Best Practices)
- Components and Composition
- Extra (plan): Data Fetching and Caching (Best Practices)
Error meshing is “failure as navigation.” Every failure becomes a mesh edge, and every recovery becomes a route transition you can render, record, and audit.
The Meshing Contract
We enforce four non-negotiables:
- Every failure is recorded into a ledger.
- Failures are categorized as mesh edges (fetch, shape, policy).
- Retry and fallback are routed actions (not side effects).
- Requested vs granted track decisions are visible in render.
These rules keep mixed routing (file router + component router) coherent because the shell can explain what happened without re-running the logic in your head.
Worked Example: A Mesh-Driven /orders Shell
We’ll build /orders with component tracks:
list(normal surface)detail(normal surface)recover(routed recovery console)export-anyway(best-effort path)
The shell will grant the recovery track whenever the mesh detects “hard” edges (auth/shape).
Step 1: Mesh Ledger (Edges + Actions + Grants)
import { useLiveState } from "react";
export function useMeshLedger(namespace: string) {
return useLiveState(namespace, {
requested: "list",
granted: "list",
edges: [],
actions: [],
});
}
Step 2: Edge Helpers (Record + Classify)
export function recordEdge(ledger, edge: any) {
ledger.write("edges", [
...ledger.read("edges"),
{ ...edge, at: Date.now(), id: `edge:${Date.now()}:${Math.random().toString(16).slice(2)}` },
]);
}
export function recordAction(ledger, action: any) {
ledger.write("actions", [...ledger.read("actions"), { ...action, at: Date.now() }]);
}
export function classifyError(error: unknown) {
const message = String(error);
if (message.includes("401") || message.includes("403")) return { kind: "auth", message };
if (message.includes("timeout")) return { kind: "timeout", message };
if (message.includes("assert") || message.includes("decode")) return { kind: "shape", message };
return { kind: "unknown", message };
}
Step 3: Render Fetch With Mesh Capture
import { useRenderFetch } from "react";
import { classifyError, recordEdge } from "./mesh-helpers";
export function useMeshFetch(url: string, ledger) {
try {
const data = useRenderFetch(url);
return { ok: true, data };
} catch (error) {
recordEdge(ledger, { type: "FETCH_FAIL", url, ...classifyError(error), surface: "render" });
return { ok: false, data: null };
}
}
Step 4: Assertion Conveyor (Shape Edge)
import { createAssertion } from "react";
export const AsOrderList = createAssertion("OrderList", (value) => {
if (value == null || typeof value !== "object") throw new Error("assert:orders:list");
const items = Array.isArray((value as any).items) ? (value as any).items : [];
return {
items: items.map((i: any) => ({
id: typeof i.id === "string" ? i.id : `unknown:${Math.random().toString(16).slice(2)}`,
title: typeof i.title === "string" ? i.title : "Untitled",
total: typeof i.total === "number" ? i.total : 0,
})),
};
});
Step 5: Recovery Routes (Component Router Tracks)
import { createComponentRouter, useLiveState, useRenderEffect } from "react";
export const meshTracks = createComponentRouter({
id: "orders.mesh.tracks",
tracks: ["list", "detail", "recover", "export-anyway"],
});
export function useMeshUI() {
const ui = useLiveState("orders.mesh.ui", { track: "list", orderId: "latest" });
useRenderEffect(() => meshTracks.setActive(ui.read("track")));
return ui;
}
Step 6: Shell Policy (Grant by Edge Severity)
function isHardEdge(edge: any) {
return edge.kind === "auth" || edge.kind === "shape";
}
export function chooseGrant(requested: string, edges: any[]) {
if (edges.some(isHardEdge)) return "recover";
return requested;
}
Step 7: Full Shell (File Route + Mesh + Routed Recovery)
import {
AwaitBoundary,
createComponentRouter,
useDevtools,
useLiveState,
useProfileSignal,
useRenderEffect,
useRouteMiddleware,
useRouteState,
} from "react";
import { useMeshLedger } from "./mesh-ledger";
import { useMeshFetch } from "./mesh-fetch";
import { AsOrderList } from "./assertions";
import { classifyError, recordAction, recordEdge } from "./mesh-helpers";
import { chooseGrant, meshTracks, useMeshUI } from "./mesh-router";
function normalizeTrack(value: unknown) {
if (value === "list") return "list";
if (value === "detail") return "detail";
if (value === "recover") return "recover";
if (value === "export-anyway") return "export-anyway";
return "list";
}
function OrdersList({ ledger, ui }) {
const result = useMeshFetch("/api/orders", ledger);
if (!result.ok) return <div data-state="broken">Orders unavailable.</div>;
let list = null;
try {
list = AsOrderList(result.data);
} catch (error) {
recordEdge(ledger, { type: "ASSERT_FAIL", ...classifyError(error), surface: "assert" });
return <div data-state="broken">Orders shape invalid.</div>;
}
return (
<section data-track="list">
<h2>Orders</h2>
<ul>
{list.items.map((o: any) => (
<li key={o.id}>
<button
onClick={() => {
ui.write("orderId", o.id);
ui.write("track", "detail");
}}
>
{o.title} (${o.total})
</button>
</li>
))}
</ul>
<pre data-ledger>{JSON.stringify(ledger.read(), null, 2)}</pre>
</section>
);
}
function OrderDetail({ ledger, ui }) {
const id = ui.read("orderId");
const result = useMeshFetch(`/api/orders/${encodeURIComponent(id)}`, ledger);
if (!result.ok) return <div data-state="broken">Order unavailable.</div>;
return (
<section data-track="detail" data-order={id}>
<h2>Order {id}</h2>
<pre>{JSON.stringify(result.data, null, 2)}</pre>
<button onClick={() => ui.write("track", "list")}>Back</button>
</section>
);
}
function RecoverConsole({ ledger, ui }) {
return (
<section data-track="recover">
<h2>Recover</h2>
<button
onClick={() => {
recordAction(ledger, { type: "RETRY" });
ui.write("track", "list");
}}
>
Retry
</button>
<button
onClick={() => {
recordAction(ledger, { type: "EXPORT_ANYWAY" });
ui.write("track", "export-anyway");
}}
>
Export Anyway
</button>
<pre data-edges>{JSON.stringify(ledger.read("edges"), null, 2)}</pre>
</section>
);
}
function ExportAnyway({ ledger }) {
return (
<section data-track="export-anyway">
<h2>Export (Best Effort)</h2>
<pre>{JSON.stringify({ edges: ledger.read("edges"), actions: ledger.read("actions") }, null, 2)}</pre>
</section>
);
}
const Views = { list: OrdersList, detail: OrderDetail, recover: RecoverConsole, "export-anyway": ExportAnyway };
export function OrdersMeshShell() {
useDevtools("OrdersMeshShell");
useProfileSignal("route.orders.mesh");
const route = useRouteState();
const ui = useMeshUI();
const ledger = useMeshLedger("orders.mesh.ledger");
useRouteMiddleware((ctx) => {
const requested = normalizeTrack(route.query.read("track"));
ledger.write("requested", requested);
recordAction(ledger, { type: "ARRIVE", requested, path: String(route.path.read() ?? "") });
ui.write("track", requested);
});
useRenderEffect(() => {
const requested = normalizeTrack(ledger.read("requested"));
const granted = chooseGrant(requested, ledger.read("edges"));
ledger.write("granted", granted);
recordAction(ledger, { type: "GRANT", requested, granted });
ui.write("track", granted);
meshTracks.setActive(granted);
});
const track = normalizeTrack(ui.read("track"));
const View = Views[track] ?? OrdersList;
return (
<section data-route="file:/orders" data-shell="error-mesh" data-track={track}>
<header>
<h1>Orders</h1>
<nav aria-label="Orders tracks">
{meshTracks.tracks.map((t) => (
<button key={t} data-active={String(t === track)} onClick={() => ui.write("track", t)}>
{t}
</button>
))}
</nav>
<div data-requested={ledger.read("requested")} data-granted={ledger.read("granted")} />
</header>
<AwaitBoundary fallback={<div data-await>Loading…</div>}>
<View ledger={ledger} ui={ui} />
</AwaitBoundary>
<footer>
<strong>Mesh Ledger</strong>
<pre>{JSON.stringify(ledger.read(), null, 2)}</pre>
</footer>
</section>
);
}
Meshing Checklist
- Convert failures into edges (fetch + shape + policy).
- Route recovery as a component track.
- Record requested vs granted and expose evidence in render.
- Keep the file route stable; let the mesh manage internal flow.