[GH-ISSUE #962] Route-level boundary nesting order diverges from Next.js #208

Closed
opened 2026-05-06 12:38:11 +02:00 by BreizhHardware · 1 comment

Originally created by @Divkix on GitHub (Apr 29, 2026).
Original GitHub issue: https://github.com/cloudflare/vinext/issues/962

Identified during review of #945.

Context

At the route level in app-page-route-wiring.tsx (around line 567-604), the ErrorBoundary sits inside the access fallback boundaries (NotFoundBoundary, ForbiddenBoundary, UnauthorizedBoundary). In Next.js, ErrorBoundary is always outermost.

This was already wrong for NotFoundBoundary before PR #945, but adding ForbiddenBoundary and UnauthorizedBoundary outside ErrorBoundary amplifies the divergence.

Current nesting (vinext, wrong)

NotFoundBoundary
  ForbiddenBoundary
    UnauthorizedBoundary
      ErrorBoundary
        children

Expected nesting (Next.js)

ErrorBoundary
  NotFoundBoundary
    ForbiddenBoundary
      UnauthorizedBoundary
        children

Suggested approach

Reorder the boundary wrapping in wrapRouteChildren in app-page-route-wiring.tsx so that ErrorBoundary wraps the access fallback boundaries.

Originally created by @Divkix on GitHub (Apr 29, 2026). Original GitHub issue: https://github.com/cloudflare/vinext/issues/962 Identified during review of #945. ## Context At the route level in `app-page-route-wiring.tsx` (around line 567-604), the ErrorBoundary sits inside the access fallback boundaries (NotFoundBoundary, ForbiddenBoundary, UnauthorizedBoundary). In Next.js, ErrorBoundary is always outermost. This was already wrong for NotFoundBoundary before PR #945, but adding ForbiddenBoundary and UnauthorizedBoundary outside ErrorBoundary amplifies the divergence. ## Current nesting (vinext, wrong) ``` NotFoundBoundary ForbiddenBoundary UnauthorizedBoundary ErrorBoundary children ``` ## Expected nesting (Next.js) ``` ErrorBoundary NotFoundBoundary ForbiddenBoundary UnauthorizedBoundary children ``` ## Suggested approach Reorder the boundary wrapping in `wrapRouteChildren` in `app-page-route-wiring.tsx` so that ErrorBoundary wraps the access fallback boundaries.
Author
Owner

@Divkix commented on GitHub (Apr 29, 2026):

Root Cause

In packages/vinext/src/server/app-page-route-wiring.tsx lines 555–571, the route-level boundary wrapping builds the wrong nesting order. ErrorBoundary is applied first (line 561), then NotFoundBoundary wraps it (line 569). This produces:

NotFoundBoundary          ← outermost (wrong)
  ErrorBoundary
    children               ← innermost

Expected Behavior (Next.js)

Next.js's create-component-tree.tsx has an explicit comment at line 579:

"Next.js nesting per segment (outer to inner): Layout > Template > Error > NotFound > children."

Building bottom-up (innermost first), the correct order is:

ErrorBoundary             ← outermost
  NotFoundBoundary
    children               ← innermost

What to Fix

In app-page-route-wiring.tsx, swap the order of the two if blocks so NotFoundBoundary is applied first, then ErrorBoundary wraps it:

  1. Move the NotFoundBoundary wrapping (lines 564–571) above the ErrorBoundary wrapping (lines 559–562)
  2. Apply the same fix at the segment level (lines 573–599) — the NotFoundBoundary block (lines 582–591) wraps before ErrorBoundary (lines 593–598), so they need swapping there too

Next.js Reference

packages/next/src/server/app-render/create-component-tree.tsx line 136 — imports HTTPAccessFallbackBoundary which houses NotFound/Forbidden/Unauthorized. The segment-level nesting at lines 1065–1090 shows HTTPAccessFallbackBoundary wrapping inner clientSegment, with ErrorBoundary at the level above. Error is always outermost relative to NotFound.

<!-- gh-comment-id:4347077879 --> @Divkix commented on GitHub (Apr 29, 2026): ## Root Cause In `packages/vinext/src/server/app-page-route-wiring.tsx` lines 555–571, the route-level boundary wrapping builds the wrong nesting order. ErrorBoundary is applied *first* (line 561), then NotFoundBoundary wraps it (line 569). This produces: ``` NotFoundBoundary ← outermost (wrong) ErrorBoundary children ← innermost ``` ## Expected Behavior (Next.js) Next.js's `create-component-tree.tsx` has an explicit comment at line 579: > "Next.js nesting per segment (outer to inner): Layout > Template > **Error > NotFound** > children." Building bottom-up (innermost first), the correct order is: ``` ErrorBoundary ← outermost NotFoundBoundary children ← innermost ``` ## What to Fix In `app-page-route-wiring.tsx`, swap the order of the two `if` blocks so NotFoundBoundary is applied first, then ErrorBoundary wraps it: 1. Move the NotFoundBoundary wrapping (lines 564–571) **above** the ErrorBoundary wrapping (lines 559–562) 2. Apply the same fix at the segment level (lines 573–599) — the `NotFoundBoundary` block (lines 582–591) wraps before `ErrorBoundary` (lines 593–598), so they need swapping there too ## Next.js Reference `packages/next/src/server/app-render/create-component-tree.tsx` line 136 — imports `HTTPAccessFallbackBoundary` which houses NotFound/Forbidden/Unauthorized. The segment-level nesting at lines 1065–1090 shows `HTTPAccessFallbackBoundary` wrapping inner `clientSegment`, with `ErrorBoundary` at the level above. Error is always outermost relative to NotFound.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#208
No description provided.