How to Fix: notfound() uses fallback instead of app/not-found.tsx with route group (turbopack)

6 min read

When notFound() renders the wrong 404 in Next.js with a route group under Turbopack, the problem is usually not your component code at all—it is the interaction between the app router’s not-found boundary resolution and a development-time bug in how grouped segments are handled.

The reported issue appears when calling notFound() inside an app route while running next dev –turbopack. Instead of rendering app/not-found.tsx, Next.js falls back to the generic 404 UI. This is especially visible when the route lives inside a route group such as (group).

If you reproduce the issue from the linked repository, you will notice an important pattern: the bug happens in development mode with Turbopack, not because your not-found.tsx file is invalid, but because the router boundary lookup does not correctly resolve the expected custom not-found component through the grouped segment tree.

That means the practical fix is to either avoid the problematic route structure temporarily, move the not-found boundary closer to the affected segment, or use a runtime/build path that does not trigger the bug until the upstream framework fix lands.

Understanding the Root Cause

In the Next.js App Router, calling notFound() throws a special internal signal. The framework catches that signal and walks the current route tree to find the nearest matching not-found.tsx boundary.

Under normal conditions, this resolution works like this:

  • A page or layout calls notFound().
  • Next.js searches upward through the active segment hierarchy.
  • If a segment-level not-found.tsx exists, it renders that boundary.
  • If no matching boundary is found, Next.js renders the default fallback 404.

The issue here is that route groups are filesystem-only organizational segments. They do not appear in the URL, but they do affect how the app directory tree is represented internally. In the affected Turbopack dev path, the not-found boundary resolution can incorrectly skip or fail to associate app/not-found.tsx with the current route when the route is nested under a grouped segment.

So the root cause is best described as:

  • A development-time Turbopack bug in route boundary resolution.
  • Triggered when notFound() is used inside routes organized with a route group.
  • Causing Next.js to render the fallback 404 instead of the expected custom app/not-found.tsx.

This is why the issue can be confusing: your app has the correct file, the route exists, and notFound() is being called properly—yet the wrong UI renders.

Step-by-Step Solution

Because this is an upstream framework bug, the most reliable approach is a workaround strategy. Use one of the following fixes depending on how much you can change your routing structure.

1. Verify your baseline file structure

First confirm that your custom 404 boundary exists in the expected place.

app/
  not-found.tsx
  (marketing)/
    page.tsx

A minimal custom not-found component should look like this:

export default function NotFound() {
  return <h1>Custom 404</h1>
}

And a page triggering it:

import { notFound } from 'next/navigation'

export default function Page() {
  notFound()
}

If this still renders the generic fallback only in next dev –turbopack, you are hitting the known issue.

2. Preferred workaround: add a closer not-found boundary inside the route group

The safest workaround is to place a not-found.tsx inside the affected grouped segment so boundary lookup does not depend on the root-level match.

app/
  not-found.tsx
  (marketing)/
    not-found.tsx
    page.tsx

Example:

export default function GroupNotFound() {
  return <div>This group-specific 404 is rendered correctly.</div>
}

This works because the router can often resolve the segment-local boundary more reliably than the root boundary in the buggy path.

3. Alternative workaround: temporarily remove the route group

If the route group is only being used for organization and is not essential, flatten the route structure.

app/
  not-found.tsx
  page.tsx

Instead of:

app/
  not-found.tsx
  (group)/
    page.tsx

This avoids the exact resolution path that triggers the issue.

4. Use Webpack-based dev mode if you need correct behavior immediately

If your team needs consistent local development behavior before the framework patch is available, run standard development mode without Turbopack.

next dev

Or in package.json:

{
  "scripts": {
    "dev": "next dev"
  }
}

This is often the best short-term move for teams blocked by misleading 404 behavior during debugging.

5. Keep production validation separate from Turbopack dev behavior

Since this issue is tied to a specific runtime path, validate whether production output is actually affected.

next build
next start

If production behaves correctly, you can classify this as a development-environment bug and avoid unnecessary app-level refactors.

If you want a robust app structure that survives this issue cleanly, use explicit boundaries where grouped routes have distinct UX.

app/
  not-found.tsx
  (shop)/
    not-found.tsx
    products/
      [slug]/
        page.tsx
  (marketing)/
    not-found.tsx
    page.tsx

This gives each logical section its own not-found boundary and reduces ambiguity even after the upstream bug is fixed.

7. Track the framework fix

Because the issue is upstream, the long-term solution is to upgrade once the fix lands in Next.js. Monitor the issue thread and release notes through the Next.js issue tracker and official release updates.

Common Edge Cases

1. not-found.tsx exists, but the file is in the wrong segment

Remember that boundary lookup is segment-based. A misplaced file such as a sibling boundary instead of a parent boundary may never be selected.

app/
  (group-a)/
    page.tsx
  (group-b)/
    not-found.tsx

In this case, (group-b) does not handle errors from (group-a).

2. You are testing with notFound() in one route and expecting a root boundary from another tree branch

Parallel route structures, nested layouts, and grouped segments can make the active boundary tree non-obvious. Always test from the exact route branch that throws the not-found signal.

3. The generic 404 appears only in dev, causing false production alarms

This is a common trap. Teams may assume routing is broken globally when only the Turbopack dev server is affected. Always compare:

  • next dev –turbopack
  • next dev
  • next build & next start

4. Dynamic routes can mask the issue

In a route like app/(shop)/products/[slug]/page.tsx, you may think slug handling is wrong when the real problem is boundary resolution after calling notFound() for a missing record.

import { notFound } from 'next/navigation'

export default async function ProductPage({ params }) {
  const product = await getProduct(params.slug)

  if (!product) {
    notFound()
  }

  return <div>{product.name}</div>
}

If this renders a fallback 404 only under Turbopack dev, your data logic is probably fine.

5. Multiple custom not-found boundaries can hide which one is being rendered

If you add several boundaries during debugging, label them clearly so you know which segment won the resolution.

export default function NotFound() {
  return <div>Marketing Group 404</div>
}

FAQ

Does this mean my app/not-found.tsx is configured incorrectly?

Usually no. If it works outside next dev –turbopack or works when the route group is removed, your configuration is likely correct. The failure is caused by a framework-level boundary resolution bug.

Should I stop using route groups entirely?

No. Route groups are still a valid and useful App Router feature. For this specific issue, either add a local not-found.tsx inside the affected group or temporarily avoid grouped structure only where the bug blocks development.

Is this a production bug or only a development bug?

In the reported case, it is primarily associated with Turbopack development mode. You should still verify your exact app with a production build, but many teams find that next build and next start behave correctly.

The shortest path to stability is this: keep your root app/not-found.tsx, add a group-local not-found.tsx where needed, use standard next dev if Turbopack blocks debugging, and upgrade Next.js once the upstream patch is released.

Leave a Reply

Your email address will not be published. Required fields are marked *