Skip to content

Turbopack: report routes nested inside catch-all segments instead of panicking in dev - #101697

Merged
lukesandberg merged 2 commits into
canaryfrom
fleet/turbopack-catch-all-illegal-route-issue
Nov 13, 2026
Merged

lukesandberg merged 2 commits into
canaryfrom
fleet/turbopack-catch-all-illegal-route-issue

Conversation

@lukesandberg

@lukesandberg lukesandberg commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A metadata file (opengraph-image, twitter-image, icon, …) or any other route placed inside a catch-all segment, e.g. app/blog/[[...slug]]/opengraph-image.tsx or app/blog/[...slug]/extra/page.tsx, crashed next dev with Turbopack:

FATAL: An unexpected Turbopack error occurred.
Error [TurbopackInternalError]: Invalid segment Static("opengraph-image"), catch all segment must be the last segment modifying the path (segments: [Static("blog"), OptionalCatchAll("slug")])

This placement is intentionally unsupported. A catch-all swallows every remaining segment, so such a route can never be matched (see #49630, #77061, #66355), and next build already rejects it with a readable error. The problem was the whole dev process aborting with an internal error.

Now Turbopack dev reports a regular app-structure error, the same way other invalid app structures are reported (e.g. conflicting route handlers):

Error: Metadata file app/blog/[[...slug]]/opengraph-image.tsx can't be used inside the catch-all segment [[...slug]]
A catch-all segment must be the last segment of a route, so the metadata route /blog/[[...slug]]/opengraph-image can never be matched. Serve the file from a Route Handler outside of the catch-all segment [[...slug]] instead. For images, reference that Route Handler with generateMetadata (for example openGraph.images, twitter.images or icons).

The dev server stays up. Requests show the error until the file is moved, and then the app recovers without a restart.

How

  • crates/next-core/src/app_structure.rs: when a metadata route or a subdirectory would add a segment after a catch-all, emit a new Error-severity NestedInCatchAllSegmentIssue (paths, routes and segments rendered as Code) and skip that route instead of propagating the Err. A directory is only reported, once at its topmost invalid level, when its subtree actually contains a page, route handler or metadata file, so plain component folders inside a catch-all stay fine. The loader-tree builder skips those directories too.
  • crates/next-core/src/next_app/mod.rs: AppPage::push (and push_str / clone_push*) now return a structured AppPageError, so callers can match SegmentAfterCatchAll { catch_all, .. } instead of re-checking. The invariant and its messages are unchanged.
  • packages/next/src/server/lib/router-utils/setup-dev-bundler.ts: with the Rust fix alone, dev still exited when such files already exist at startup. The initial getSortedRoutes throws Catch-all must be the last part of the URL. and rejects dev setup. Later rescans only log a warning. For Turbopack only, routes nested inside a catch-all are now left out of that list, since Turbopack reports them itself and creates no entrypoints for them.

Unchanged: next build (still fails via validateAppPaths) and webpack dev (still exits with its existing readable error).

Docs

  • docs/01-app/03-api-reference/03-file-conventions/01-metadata/opengraph-image.mdx: new troubleshooting section, "Using an image in a catch-all segment", with the Route Handler + generateMetadata workaround.
  • docs/01-app/03-api-reference/03-file-conventions/01-metadata/app-icons.mdx: the same note for icon / apple-icon.

I searched the other docs, error pages, tests and comments and found nothing else that states or depends on this behavior.

Finding: https://next-maintainer-agent.vercel.tools/agent-feedback/7208

Verification

  • New test/development/app-dir/catch-all-nested-routes (Turbopack only via @force-gate turbopack) covers:

    • metadata images inside [[...slug]] and [...slug]
    • a nested page
    • a nested route handler plus a static apple-icon.png
    • files present at dev startup
    • recovery to 200 after deleting the files

    It failed before the fix (dev exited with TurbopackInternalError). It passes with the fix, also with __NEXT_CACHE_COMPONENTS=true.

  • Existing suites still pass with pnpm test-dev-turbo: metadata-dynamic-routes, conflicting-page-segments, app-catch-all-optional, catchall-specificity, catchall-parallel-routes-group, parallel-routes-catchall*, interception-routes-root-catchall, interception-routes-multiple-catchall, parallel-routes-and-interception-catchall, optional-catchall-route-params.

    • After the AppPageError refactor, I re-ran metadata-dynamic-routes (23/23) and parallel-routes-catchall* (9/10). The one failure, parallel-routes-catchall-specificity (expects Profile, gets Modal), also fails locally on unmodified canary (v16.5.0-canary.49) and on this PR's first commit, so it is unrelated to this change. CI on the first commit passed it.
  • cargo test -p next-core --lib, cargo clippy -p next-core, cargo fmt --check: pass.

  • Manual: next build on an app with [[...slug]]/opengraph-image.tsx still fails with Optional catch-all must be the last part of the URL in route "/section/[[...slug]]/opengraph-image".

  • Not run: the production-mode metadata suites, since build validation is unchanged.

…n Turbopack dev

Co-authored-by: Luke Sandberg <210140+lukesandberg@users.noreply.github.com>
@vercel

vercel Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Vercel Agent finished reviewing your changes and approved this pull request.
ac3a666 • Fri, 13 Nov 2026 00:31:11 GMT

Review steps (54.7s)

Preparing the workspace 12.8s
Analyzing the changes 39.1s
Finalizing the review 2.8s

@github-actions

github-actions Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Tests Passed

Commit: ac3a666
View workflow run

@github-actions github-actions Bot added created-by: Turbopack team PRs by the Turbopack team. Documentation Related to Next.js' official documentation. tests Turbopack Related to Turbopack with Next.js. type: next labels Nov 12, 2026
Comment thread crates/next-core/src/next_app/mod.rs Resolved
Comment thread crates/next-core/src/app_structure.rs Resolved
Comment thread crates/next-core/src/app_structure.rs Resolved
Comment thread crates/next-core/src/app_structure.rs Resolved
…t issue text

Co-authored-by: Luke Sandberg <210140+lukesandberg@users.noreply.github.com>
@lukesandberg
lukesandberg marked this pull request as ready for review November 13, 2026 02:25
@lukesandberg
lukesandberg merged commit 10056ba into canary Nov 13, 2026
231 of 233 checks passed
@lukesandberg
lukesandberg deleted the fleet/turbopack-catch-all-illegal-route-issue branch November 13, 2026 02:25
jamiboym added a commit that referenced this pull request Nov 13, 2026
Resolve a conflict with #101697, which reports routes nested inside catch-all segments instead of panicking in Turbopack dev. Keep canary's isNestedInCatchAllSegment filter and this branch's exported verifyTypeScript. On this branch the filter's Turbopack case is unused, because Turbopack dev takes its routes from native discovery, which already skips those routes and reports the issue.

#101697 also changed AppPage::clone_push_str to return its own error type and added a DirectoryTree destructuring in app_structure.rs. Adapt this branch's authored-file inventory to both: metadata nested inside a catch-all segment stays in the inventory without a runtime URL, and the new destructuring ignores the inventory fields.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

created-by: Turbopack team PRs by the Turbopack team. Documentation Related to Next.js' official documentation. tests Turbopack Related to Turbopack with Next.js. type: next

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant