AI & Agent Dev Bug Sandbox logo
AI & Agent Dev Bug Sandbox
Back to Radar

Route Group Layout Leaks Into Root `Not-Found` Page

When `notFound()` is called inside a nested route that belongs to a route group, the root `not-found` page incorrectly renders inside the route group's layout instead of only within the root layout.

mediumConfidence 75%Next.jsAffected V16.3.0-Preview.10

Origin Analysis

Next.js App Router's not-found boundary resolution treats route group segments as regular layout segments even when the route group does not define its own `not-found.tsx`. This causes the group's layout to be included in the ancestor chain of the root not-found boundary.
1. Create an app with `app/(route-group)/layout.tsx`, `app/(route-group)/route-segment/[slug]/page.tsx` that calls `notFound()`, and a root `app/not-found.tsx`. 2. Run `pnpm dev` or `pnpm build && pnpm start`. 3. Visit `/route-segment/error`. 4. Observe that the root not-found page is wrapped inside the `(route-group)` layout.

Fixing Code Block

diff --git a/packages/next/src/server/app-render/not-found-boundary.tsx b/packages/next/src/server/app-render/not-found-boundary.tsx new file mode 100644 index 0000000..0000000 --- /dev/null +++ b/packages/next/src/server/app-render/not-found-boundary.tsx @@ -0,0 +1,32 @@ +import { notFound as notFoundError } from '../../client/components/not-found' + +export function getNotFoundBoundary(segmentPath: string[]) { + // Skip route group segments (denoted by parentheses) when they do not + // contain a not-found file. This prevents their layout from leaking into + // the root not-found boundary. + const filteredPath = segmentPath.filter((segment, index) => { + const isRouteGroup = segment.startsWith('(') && segment.endsWith(')') + if (!isRouteGroup) return true + // Check if the route group has its own not-found component by looking + // at the segment's file system entry. This is a simplified check; the + // actual Next.js implementation must use the build manifest. + const hasOwnNotFound = segmentPath + .slice(0, index + 1) + .some((s) => s === 'not-found') + return hasOwnNotFound + }) + return filteredPath +}
The patch filters out route group segments from the not-found boundary calculation unless the route group itself defines a not-found file. This ensures that the root not-found page only inherits layouts from segments that are explicitly part of the URL path or have their own not-found boundary.

Edge Case Audit

The provided diff is a simplified hotfix and may not compile against the actual Next.js 16.3.0-preview.10 source without adaptation. It must be integrated into the real not-found resolution logic, which relies on the build manifest and loader context. Incorrect integration could break nested not-found pages inside route groups that do define their own not-found files. Before applying, ensure a full test suite passes, and keep the change isolated to allow easy rollback by reverting the patch.

Ecosystem Topology