D
P
0

Next.js

`next dev` Turbopack Crashes With `invariant expected layout router to be mounted` on One Machine Only? A `node_modules` Junction Is the Culprit

July 8, 2026·5 min read
`next dev` Turbopack Crashes With `invariant expected layout router to be mounted` on One Machine Only? A `node_modules` Junction Is the Culprit

I was building a feature in a Next.js app across two parallel worktrees on my Windows machine. Everything was fine until I ran next dev in the second worktree, and Turbopack fell over immediately. No warning, no tidy error page in the browser — an actual crash in the terminal with a message that stopped me cold:

invariant expected layout router to be mounted

The strange part: the first worktree ran flawlessly. The production deploy was healthy too, the live site had no issues. A local next build passed without a single complaint. Only next dev, on one machine, in one worktree, blew up. That combination almost always points to something in the environment, not the code.

The symptom

The error came from the App Router's internal layout-router machinery. It read as if the router tree had never mounted, even though I had not touched a single routing file. My first instinct was a bad one: I assumed a component was misusing a client hook on the server, or that the next version was out of sync between worktrees. So I checked the usual suspects.

I diffed package.json and the lockfile across worktrees — identical. I checked the resolved next version — exactly the same. I deleted .next, restarted, the error came right back. I even opened the same code in the healthy first worktree, and it ran without issue. The files were identical. The only thing that differed was how node_modules arrived in the second worktree.

The investigation

To save disk across parallel worktrees, I did not run npm install again in each folder. I created node_modules in the second worktree as a Windows directory junction pointing at a node_modules elsewhere, outside the project root. An old trick, usually nobody cares, and it had genuinely been safe for years.

I confirmed the hunch by inspecting the folder:

fsutil reparsepoint query node_modules

Sure enough, node_modules was not a real folder but a reparse point resolving to a path outside the project root. At that point the symptom pattern started to make sense: Webpack does not care, but Turbopack does. The default next build still used the older compilation path, so it passed. next dev, which now defaults to Turbopack, exploded.

The root cause

Turbopack resolves modules through the real filesystem path, not the logical path you typed. When node_modules is a junction, the resolved path "escapes" the project root and lands at the junction's real location. To Turbopack, that means some dependencies appear to live outside the project boundary. React and next can end up resolved through two different path identities, and once the App Router sees two copies of React that are not the same by path, its router context fractures. The result is exactly that invariant: the layout router looks like it never mounted, because the instance that mounts it and the instance that reads the context are not the same React.

So this was neither a code bug nor a Next.js regression. It was purely a junction artifact. The first worktree was healthy because its node_modules was a real folder. Production was healthy because it built in a clean environment with no junction. Only my machine, only the worktree whose disk I had cleverly deduplicated, was affected.

The fix

There are two routes, depending on whether you want to keep the junction or not.

When I need dev running quickly and do not want to dismantle the disk setup, I force Turbopack back to the Webpack compiler. Webpack tolerates the junction because its resolution is not as strict about the real path:

next dev --webpack

With that flag, the dev server was instantly normal again and the router error vanished entirely. This is a legitimate patch when you are deliberately using a junction and accept the trade-off.

The genuinely clean fix is to remove the junction and install a real local node_modules. I deleted the reparse point, then reinstalled from the lockfile for a deterministic tree:

rmdir node_modules
npm ci

After a real install, node_modules is an actual folder inside the project root, its resolved paths no longer escape the boundary, and next dev with default Turbopack runs normally with no flags at all. The only trade-off is disk that is no longer shared between worktrees. For me that is a cheap price compared to chasing a phantom invariant every time I switch worktrees.

Checklist

If next dev (Turbopack) crashes with invariant expected layout router to be mounted while the build and production are healthy, trace it this way:

  • Check whether the crash is limited to one machine or one folder, not CI or the deploy. That strongly signals an environment problem, not code.
  • Verify whether node_modules is a real folder or a junction with fsutil reparsepoint query node_modules. If the reparse point points outside the project root, that is your prime suspect.
  • Patch quickly with next dev --webpack to confirm Turbopack is the culprit. If Webpack runs and Turbopack does not, the theory holds.
  • Fix it properly with rmdir node_modules then npm ci so a real local node_modules lives inside the root.
  • Remember: the default next build can pass even when next dev crashes, because the two can use different compilation paths. Do not treat a green build as proof that dev is healthy.

My lesson here is simple: Turbopack has stricter assumptions about filesystem boundaries than Webpack does. A junction that was safe for years as a disk-saving trick can quietly violate those assumptions, and the symptom disguises itself as a router bug that has nothing to do with the actual cause. Since then, whenever an error shows up on one machine only, my first question is no longer "which code is wrong," but "what is different about my filesystem."