nitro icon indicating copy to clipboard operation
nitro copied to clipboard

v3: Cannot build TanStack Start pnpm monorepo in SPA mode with Nitro

Open rohanajayjain opened this issue 1 month ago • 6 comments

Environment

node: 22.18.0 nitro: [email protected]

Reproduction

https://github.com/rohanajayjain/ts-start-bug

Describe the bug

Trying to build in a pnpm monorepo using TanStack Start and Better Auth in SPA mode with nitro fails.

This seems to be specifically a nitro related issue as building without the nitro plugin works.

With SPA mode disabled, nitro can build the code without errors. We can then run node --env-file=.env .output/server/index.mjs as well without issue.

I've tried without adding ssr: { external: ["pg"] } and with it and get different logs (included below).

Without adding ssr: { external: ["pg"] }, we get ReferenceError: Cannot access 'pg' before initialization.

With [ssr](ssr: { external: ["pg"] }), we get Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'pg' imported from /home/codebench/Dev/ts-start-bug/apps/web/.nitro/vite/services/ssr/assets/router-w0xdFAbu.js Did you mean to import "pg/lib/index.js"?

Additional context

No response

Logs

When ssr external isn't set:

codebench@codebench:~/Dev/ts-start-bug/apps/web$ pnpm build

> [email protected] build /home/codebench/Dev/ts-start-bug/apps/web
> vite build

vite v7.2.0 building client environment for production...
✓ 204 modules transformed.
.output/public/assets/index-DtR-hLxC.js    0.66 kB │ gzip:   0.41 kB
.output/public/assets/main-Dh3ZPKv-.js   352.12 kB │ gzip: 111.39 kB
✓ built in 977ms
vite v7.2.0 building ssr environment for production...
✓ 767 modules transformed.
.nitro/vite/services/ssr/assets/start-HYkvq4Ni.js                             0.06 kB
.nitro/vite/services/ssr/assets/_tanstack-start-manifest_v-B07cSHf9.js        0.45 kB
.nitro/vite/services/ssr/assets/index-DndBZZAl.js                             1.37 kB
.nitro/vite/services/ssr/assets/bun-sqlite-dialect-B2O0-0JW-DZaXpg95.js       4.72 kB
.nitro/vite/services/ssr/assets/node-sqlite-dialect-5DWxxNaH-CXgDQ1bH.js      4.74 kB
.nitro/vite/services/ssr/assets/index-n0yJTA-g.js                             6.41 kB
.nitro/vite/services/ssr/assets/index-y4iGJYqZ.js                             8.27 kB
.nitro/vite/services/ssr/server.js                                          116.39 kB
.nitro/vite/services/ssr/assets/router-ugILWlS3.js                        1,819.28 kB
✓ built in 2.08s
[prerender] Prerendering pages...
[prerender] Concurrency: 8
[prerender] Crawling: /
ReferenceError: Cannot access 'pg' before initialization
    at get default (file:///home/codebench/Dev/ts-start-bug/apps/web/.nitro/vite/services/ssr/assets/router-ugILWlS3.js:59:5)
    ... 8 lines matching cause stack trace ...
    at async file:///home/codebench/Dev/ts-start-bug/node_modules/.pnpm/@[email protected]_@[email protected][email protected]_r_967f5b275724ce77a0f689c31de802f9/node_modules/@tanstack/start-plugin-core/dist/esm/queue.js:38:17 {
  cause: ReferenceError: Cannot access 'pg' before initialization
      at get default (file:///home/codebench/Dev/ts-start-bug/apps/web/.nitro/vite/services/ssr/assets/router-ugILWlS3.js:59:5)
      at getAugmentedNamespace (file:///home/codebench/Dev/ts-start-bug/apps/web/.nitro/vite/services/ssr/assets/router-ugILWlS3.js:47956:13)
      at file:///home/codebench/Dev/ts-start-bug/apps/web/.nitro/vite/services/ssr/assets/router-ugILWlS3.js:52097:36
      at ModuleJob.run (node:internal/modules/esm/module_job:343:25)
      at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:647:26)
      at async getEntries (file:///home/codebench/Dev/ts-start-bug/apps/web/.nitro/vite/services/ssr/server.js:3263:21)
      at async startRequestResolver (file:///home/codebench/Dev/ts-start-bug/apps/web/.nitro/vite/services/ssr/server.js:3320:33)
      at async localFetch (file:///home/codebench/Dev/ts-start-bug/node_modules/.pnpm/@[email protected]_@[email protected][email protected]_r_967f5b275724ce77a0f689c31de802f9/node_modules/@tanstack/start-plugin-core/dist/esm/prerender.js:63:22)
      at async file:///home/codebench/Dev/ts-start-bug/node_modules/.pnpm/@[email protected]_@[email protected][email protected]_r_967f5b275724ce77a0f689c31de802f9/node_modules/@tanstack/start-plugin-core/dist/esm/prerender.js:125:23
      at async file:///home/codebench/Dev/ts-start-bug/node_modules/.pnpm/@[email protected]_@[email protected][email protected]_r_967f5b275724ce77a0f689c31de802f9/node_modules/@tanstack/start-plugin-core/dist/esm/queue.js:38:17,
  status: 500,
  statusText: undefined,
  headers: undefined,
  data: undefined,
  body: undefined,
  unhandled: true
}
[prerender] Prerendered 0 pages:
[sitemap] Hint: Pages found, but no sitemap host has been set. To enable sitemap generation, set the `sitemap.host` option.

[nitro 10:56:32 am] ◐ Building [Nitro] (preset: node-server, compatibility: 2025-11-06)
file:///home/codebench/Dev/ts-start-bug/node_modules/.pnpm/@[email protected]_@[email protected][email protected]_r_967f5b275724ce77a0f689c31de802f9/node_modules/@tanstack/start-plugin-core/dist/esm/prerender.js:138
            throw new Error(`Failed to fetch ${page.path}: ${res.statusText}`, {
                  ^

Error: Failed to fetch /: Internal Server Error
    at file:///home/codebench/Dev/ts-start-bug/node_modules/.pnpm/@[email protected]_@[email protected][email protected]_r_967f5b275724ce77a0f689c31de802f9/node_modules/@tanstack/start-plugin-core/dist/esm/prerender.js:138:19
    at async file:///home/codebench/Dev/ts-start-bug/node_modules/.pnpm/@[email protected]_@[email protected][email protected]_r_967f5b275724ce77a0f689c31de802f9/node_modules/@tanstack/start-plugin-core/dist/esm/queue.js:38:17 {
  [cause]: NodeResponse$1 [Response] {}
}

Node.js v22.18.0
 ELIFECYCLE  Command failed with exit code 1.`

---

With ssr external with ['pg']:
codebench@codebench:~/Dev/ts-start-bug/apps/web$ pnpm build

> [email protected] build /home/codebench/Dev/ts-start-bug/apps/web
> vite build

vite v7.2.0 building client environment for production...
✓ 204 modules transformed.
.output/public/assets/index-DtR-hLxC.js    0.66 kB │ gzip:   0.41 kB
.output/public/assets/main-Dh3ZPKv-.js   352.12 kB │ gzip: 111.39 kB
✓ built in 993ms
vite v7.2.0 building ssr environment for production...
✓ 685 modules transformed.
.nitro/vite/services/ssr/assets/start-HYkvq4Ni.js                             0.06 kB
.nitro/vite/services/ssr/assets/_tanstack-start-manifest_v-B07cSHf9.js        0.45 kB
.nitro/vite/services/ssr/assets/index-XYXuMfZ2.js                             1.23 kB
.nitro/vite/services/ssr/assets/bun-sqlite-dialect-B2O0-0JW-C9efU9gy.js       4.57 kB
.nitro/vite/services/ssr/assets/node-sqlite-dialect-5DWxxNaH-B2JIiB1Z.js      4.59 kB
.nitro/vite/services/ssr/assets/index-DP2UTXPC.js                             6.26 kB
.nitro/vite/services/ssr/assets/index-Dv7V5yx9.js                             8.12 kB
.nitro/vite/services/ssr/server.js                                          116.39 kB
.nitro/vite/services/ssr/assets/router-w0xdFAbu.js                        1,653.52 kB
✓ built in 1.86s
[prerender] Prerendering pages...
[prerender] Concurrency: 8
[prerender] Crawling: /
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'pg' imported from /home/codebench/Dev/ts-start-bug/apps/web/.nitro/vite/services/ssr/assets/router-w0xdFAbu.js
Did you mean to import "pg/lib/index.js"?
    at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
    ... 7 lines matching cause stack trace ...
    at ModuleJob._link (node:internal/modules/esm/module_job:183:49) {
  cause: Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'pg' imported from /home/codebench/Dev/ts-start-bug/apps/web/.nitro/vite/services/ssr/assets/router-w0xdFAbu.js
  Did you mean to import "pg/lib/index.js"?
      at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
      at packageResolve (node:internal/modules/esm/resolve:767:81)
      at moduleResolve (node:internal/modules/esm/resolve:853:18)
      at defaultResolve (node:internal/modules/esm/resolve:983:11)
      at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:783:12)
      at #cachedDefaultResolve (node:internal/modules/esm/loader:707:25)
      at ModuleLoader.resolve (node:internal/modules/esm/loader:690:38)
      at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:307:38)
      at ModuleJob._link (node:internal/modules/esm/module_job:183:49) {
    code: 'ERR_MODULE_NOT_FOUND'
  },
  status: 500,
  statusText: undefined,
  headers: undefined,
  data: undefined,
  body: undefined,
  unhandled: true
}
[prerender] Prerendered 0 pages:
[sitemap] Hint: Pages found, but no sitemap host has been set. To enable sitemap generation, set the `sitemap.host` option.

[nitro 11:00:13 am] ◐ Building [Nitro] (preset: node-server, compatibility: 2025-11-06)
file:///home/codebench/Dev/ts-start-bug/node_modules/.pnpm/@[email protected]_@[email protected][email protected]_r_967f5b275724ce77a0f689c31de802f9/node_modules/@tanstack/start-plugin-core/dist/esm/prerender.js:138
            throw new Error(`Failed to fetch ${page.path}: ${res.statusText}`, {
                  ^

Error: Failed to fetch /: Internal Server Error
    at file:///home/codebench/Dev/ts-start-bug/node_modules/.pnpm/@[email protected]_@[email protected][email protected]_r_967f5b275724ce77a0f689c31de802f9/node_modules/@tanstack/start-plugin-core/dist/esm/prerender.js:138:19
    at async file:///home/codebench/Dev/ts-start-bug/node_modules/.pnpm/@[email protected]_@[email protected][email protected]_r_967f5b275724ce77a0f689c31de802f9/node_modules/@tanstack/start-plugin-core/dist/esm/queue.js:38:17 {
  [cause]: NodeResponse$1 [Response] {}
}

Node.js v22.18.0
 ELIFECYCLE  Command failed with exit code 1.

rohanajayjain avatar Nov 06 '25 00:11 rohanajayjain

Thanks for reporting issue with good reproduction!

I have tried latest nitro-nightly build. Adding pg dependency directly to your project solves the issue.

The issue is that, vite generates SSR bundle first, resolves nested dependencies and marks pg as external. Then second nitro vite build step, sees a bare "pg" import which cannot resolve due to pnpm hoisting.

Proper fix for this would be that nitro should use a special plugin that resolves externals in intermediate "ssr" bundle to absolute paths. We had been using this solution for Nuxt and Nitro v2.

pi0 avatar Nov 10 '25 10:11 pi0

@pi0 I have a pnpm project with a dependency structure like: app > devDependency X -> dependency Y -> minio

nitro v3 fails to build the app and tells me that I have to set minio as a rollup external. If I do that the build succeeds, but the server wont start as it cant find minio, unless I manually install it as a direct dependency of the app. In nitro v2 minio was automatically copied into the dist/output folder and build/start just worked.

Is my issue the same as this one here and fixed by the proposed Proper fix for this, or should I make a new issue for mine? 🤔

katywings avatar Nov 16 '25 11:11 katywings

@katywings a new issue with reproduction or your project source would be ideal.

Also please try noExternals: true config. We likely default to this option (it is vite that bundles code not nitro)

pi0 avatar Nov 16 '25 12:11 pi0

@pi0 Great news: I noticed that I had minio, bcrypt and a couple others in resolve.external and optimizeDeps.exclude. By removing minio from said options the build error went away.

Interestingly enough for the other server deps like bcrypt nitro seems to include these just fine in the traced-node-modules even though I didn't remove them from resolve.external and optimizeDeps.exclude.

Btw noExternals: true also fixed the issue with minio - but broke the really non-bundleable dependencies like prisma 6 😉.

katywings avatar Nov 17 '25 18:11 katywings

Thnx for confirming. There we an option to opt-in incompatible dependencies like prisma 6 to be traced instead of bundled.

pi0 avatar Nov 17 '25 20:11 pi0

Running into the same issue myself, I'm unable to turn on SPA mode in tanstack start

jacobgad avatar Nov 25 '25 22:11 jacobgad