Skip to content

fix(next-dev): don't let an app dir without real routes override pages/404#95713

Open
thsid wants to merge 2 commits into
vercel:canaryfrom
thsid:codex/fix-pages-404-empty-app-dir
Open

fix(next-dev): don't let an app dir without real routes override pages/404#95713
thsid wants to merge 2 commits into
vercel:canaryfrom
thsid:codex/fix-pages-404-empty-app-dir

Conversation

@thsid

@thsid thsid commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

What?

Fixes a bug where the mere presence of an app directory — even one with no real routes in it — would override pages/404, instead of falling back to it as expected.

Why?

The 404 page resolution logic treated the existence of the app directory itself as a signal that an app-dir 404 should take precedence, rather than checking whether the app dir actually defines any routes. This caused pages/404 to be silently shadowed in projects that have an (effectively empty) app directory alongside a pages directory.

How?

  • Fixed the route resolution so next dev correctly falls back to pages/404 when the app directory contains no real routes. Covers both the webpack and Turbopack dev paths.
  • next build with webpack was already handled correctly by a prior fix and required no change here.
  • Added a regression test at test/e2e/app-dir/pages-404-with-empty-app-dir.

Known remaining issue

next build --turbopack (production build) is not fixed by this PR — the root cause there is on the Rust side, in how Turbopack enumerates entrypoints, which is out of scope for this change. This case is covered by a test marked with it.failing to track it explicitly rather than leave it silently broken.

Fixes #58945

An `app` directory that exists but has no real app routes (e.g. only a
root layout) incorrectly hijacked 404 handling away from a custom
`pages/404`, because dev-mode page resolution unconditionally fell
back to the built-in not-found component whenever the `app` directory
existed, regardless of whether it had any real routes.

Fixes vercel#58945
@thsid thsid marked this pull request as ready for review July 12, 2026 01:53
Comment thread packages/next/src/server/dev/on-demand-entry-handler.ts
findPagePathData fell back to a full recursive walk of the app
directory (via collectAppFiles) to check whether it had any real
routes, on every 404 request without a custom not-found file. Since
ensurePage only dedupes concurrent calls, this ran on every distinct
404, not just once.

Add a stopAfterFirstMatch option to recursiveReadDir and a hasAppRoutes
helper that stops as soon as a single route is found, so a normal app
(with a root layout) resolves after reading just the top-level
directory instead of walking the whole tree.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom 404 not working in Pages Router if app directory exists

1 participant