From dc66bbd7154811a0cbb32167fff239238852f727 Mon Sep 17 00:00:00 2001 From: jaeho Date: Sun, 8 Mar 2026 00:38:21 +0900 Subject: [PATCH] fix(solid-router): wrap root Outlet child with CatchBoundary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When `shellComponent` renders `` directly instead of `{props.children}`, the root's CatchBoundary (from Match) is never placed in the render tree. Errors re-thrown from a child route's errorComponent have no parent boundary to propagate to. Fix: unconditionally wrap the root Outlet's child `` with a CatchBoundary using the root's errorComponent. The extra boundary is harmless when shellComponent does render children — the inner CatchBoundary (from Match) catches first. Closes #6845 Made-with: Cursor --- packages/solid-router/src/Match.tsx | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/solid-router/src/Match.tsx b/packages/solid-router/src/Match.tsx index b5dff1cd5fb..c16ae808b05 100644 --- a/packages/solid-router/src/Match.tsx +++ b/packages/solid-router/src/Match.tsx @@ -388,6 +388,11 @@ export const Outlet = () => { const shouldShowNotFound = () => childMatchStatus() !== 'redirected' && parentGlobalNotFound() + const rootErrorComponent = () => + router.routesById[rootRouteId]?.options.errorComponent ?? + router.options.defaultErrorComponent + const rootResetKey = useRouterState({ select: (s) => s.loadedAt }) + return ( { } > {(matchIdAccessor) => { - // Use a memo to avoid stale accessor errors while keeping reactivity const currentMatchId = Solid.createMemo(() => matchIdAccessor()) + const childMatch = () => + return ( } + fallback={childMatch()} > } > - + {rootErrorComponent() ? ( + rootResetKey()} + errorComponent={rootErrorComponent()!} + onCatch={(error) => { + if (isNotFound(error)) throw error + }} + > + {childMatch()} + + ) : ( + childMatch() + )} )