Conversation
… keys `ResolveResult::with_replaced_request_key` overwrote the `conditions` of every result key with the conditions of the replacement key (empty at all call sites). Those conditions are what distinguishes the results one request resolved to under different export conditions, so both the `module-sync` and the `default`/`require` target of the same subpath ended up under the same `RequestKey`. That is lossy as soon as a request resolves in more than one `node_modules` directory: the results are merged through a `RequestKey`-keyed map, so one of the two targets is silently dropped. With a single candidate directory `ResolveResult::alternatives` short-circuits and never builds that map, which is why this only showed up in nested layouts. In practice this broke `output: 'standalone'` with the Turbopack production bundler for pnpm installs, whose virtual store hoists every package into `node_modules/.pnpm/node_modules` and thereby creates that second candidate directory: `next-server.js.nft.json` recorded only `@swc/helpers/cjs/_interop_require_default.cjs`, while Node >= 22.12 resolves `@swc/helpers/_/_interop_require_default` to the `module-sync` target `esm/_interop_require_default.js` (`@swc/helpers` 0.5.23 lists `module-sync` first). The copied `.next/standalone/server.js` then exited with MODULE_NOT_FOUND before listening. Keep the existing key's conditions instead, extending them with the replacement key's. Tests: - `module-sync-condition-cjs-subpath`: a subpath export that hands `module-sync` an ESM file and `default` a CommonJS one, required from CommonJS. - `module-sync-condition-cjs-nested-symlink`: the same package reachable through two `node_modules` directories, as in a pnpm install. Fails without this fix. - `test/production/standalone-mode/swc-helpers-module-sync`: asserts that the helper target Node itself resolves is traced and copied, and that the standalone server starts and renders with nothing but the standalone output. - `next-server-nft`: the adapter snapshot gains the ESM helper it was missing. Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>
Contributor
Tests PassedCommit: 42c226d |
Contributor
Stats from current PR✅ No significant changes detected📊 All Metrics📖 Metrics GlossaryDev Server Metrics:
Build Metrics:
Change Thresholds:
⚡ Dev Server
📦 Dev Server (Webpack) (Legacy)📦 Dev Server (Webpack)
⚡ Production Builds
📦 Production Builds (Webpack) (Legacy)📦 Production Builds (Webpack)
📦 Bundle SizesBundle Sizes⚡ TurbopackClient Main Bundles
Server Middleware
Build DetailsBuild Manifests
Build Cache
📦 WebpackClient Main Bundles
Polyfills
Pages
Server Edge SSR
Middleware
Build DetailsBuild Manifests
Build Cache
🔄 Shared (bundler-independent)Runtimes
📎 Tarball URLCommit: 42c226d |
mischnic
marked this pull request as ready for review
August 14, 2026 16:20
wbinnssmith
approved these changes
Aug 14, 2026
nothink
added a commit
to nothink-jp/suzumina.click
that referenced
this pull request
Aug 17, 2026
* fix(deps): next を 16.3.0 に固定し Cloud Run の起動失敗を止める next 16.3.1 を載せた #933 以降、Deploy Web が起動プローブで落ち続けている (efdf9a51 / 36b1e6e の2回)。本番は直前の正常 revision を維持しているため サイトは動いているが、#933・#934 の web 側の変更が届いていない。 16.3.1 が同梱する @swc/helpers 0.5.23 は exports の先頭が module-sync で、 Node >= 22.12 の require は esm/_interop_require_default.js に解決する。一方 Turbopack の tracing は条件を取り違えて cjs 側しか standalone に含めないため MODULE_NOT_FOUND で exit(1) する(pnpm の仮想ストアで候補ディレクトリが 2つになると発現。上流 issue vercel/next.js#97358)。 修正 PR vercel/next.js#97372 は canary にのみ入っており 16.3.1 stable には 未収録のため、待っても直らない。16.3.1 の中身は Turbopack・キャッシュ・ prefetch の修正でセキュリティ修正は無く、本番は元々 16.3.0 で動いている。 outputFileTracingIncludes での回避も検証して動いたが(PR #936)、解除条件を コメントに書いても読む人がいない。代わりに dependabot.yml で 16.3.1 だけを 除外し、16.3.2 以降は通常どおり提案させる=解除を人の記憶に頼らない。 確認: - pnpm verify → exit 0 - standalone をビルドして起動 → /api/health 200(Next.js 16.3.0) - 16.3.0 では nft も Node も cjs 側に一致し、esm/ が無くても整合する Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(ci): bundle-analyzer を固定対象外とする根拠を残す AIレビューの指摘(next 16.3.1 を避ける意図から @next/bundle-analyzer が 漏れているのは意図的か)への回答。実測で無関係と確認できたため、 除外理由を ignore 行の隣に記録する。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
owine
added a commit
to owine/house-manager
that referenced
this pull request
Aug 18, 2026
## What's broken `main` has been red since #410 (`deps: update all non-major dependencies`). Both `build-image` jobs fail on **`scripts/smoke-image.sh`** — the web container exits during boot: ``` Error: Cannot find module '/app/web/node_modules/.pnpm/next@16.3.1_.../node_modules/@swc/helpers/esm/_interop_require_default.js' at createEsmNotFoundErr (node:internal/modules/cjs/loader:1554:15) SMOKE FAIL: smoke-web-4254 exited during boot before serving /api/health ``` #411 and #413 inherit it. PR #413's entire diff is `default-browser@5.5.0 -> 5.5.1`, so it is not the cause. ## Root cause #410 carried `next 16.3.0 -> 16.3.1`, whose changelog includes *"[backport] Bump @swc/helpers"*. That moved `@swc/helpers` `0.5.15 -> 0.5.23`, which added a **`module-sync`** condition to every `./_/*` subpath export: ```jsonc // 0.5.15 "./_/_interop_require_default": { "import": "./esm/_interop_require_default.js", "default": "./cjs/_interop_require_default.cjs" } // 0.5.23 "./_/_interop_require_default": { "module-sync": "./esm/_interop_require_default.js", // <- new "webpack": "./esm/_interop_require_default.js", // <- new "import": "./esm/_interop_require_default.js", "default": "./cjs/_interop_require_default.cjs" } ``` `next/dist` requires `@swc/helpers/_/_interop_require_default` from ~57 places. Node 22.10+/24 honours `module-sync` from CJS `require()`, so that now resolves to the **ESM** file at runtime — but the file tracer still copies only the **CJS** one into `.next/standalone`. The bundle therefore ships a `@swc/helpers` whose `package.json` resolves a subpath to a file that isn't on disk. `createEsmNotFoundErr` is the tell: exports matched, `stat` failed. ## The fix `outputFileTracingIncludes` forces `esm/` into the trace. Upstream is [vercel/next.js#97358](vercel/next.js#97358), fixed by [#97372](vercel/next.js#97372) — but that is **canary-only** (`16.3.1-canary.20`+); `16.3.1` is still `latest` and there is no `16.3.2`. Given the repo's exact-pin discipline, canary isn't an option, so this workaround stays until a stable release carries the fix. The `next.config.ts` comment says so. > [!NOTE] > [vercel/next.js#97450](vercel/next.js#97450) reports `outputFileTracingIncludes` silently no-ops under Turbopack, and we do build with Turbopack. That does **not** reproduce here — tested rather than assumed, see below. ## Verification | | esm files in `.next/standalone` | |---|---| | before | **0** | | after | **108** (incl. `_interop_require_default.js`) | Same command, only the config changed. ``` $ scripts/smoke-image.sh house-manager:smoke-fix ✓ web /api/health ✓ public/sw.js ✓ prerendered app output served ✓ dynamic render (/ → signin, no-store) ✓ .next/static asset (/_next/static/chunks/00_osidhtc0-w.css) ✓ worker /api/health SMOKE PASS ``` `pnpm verify` green (113 files, 1312 tests). ## Why nothing else caught it `tsc --noEmit`, `next build`, and every vitest/playwright suite resolve against the full source tree — only the assembled image exercises the traced bundle. This is the "green build, dead container" class `CLAUDE.md` warns about, and `smoke-image.sh` is what caught it. Its `/` probe (a real dynamic render, not `/api/health`) is exactly the check that mattered. ## Summary by Sourcery Bug Fixes: - Ensure the standalone Next.js bundle includes the SWC helper ESM files required at runtime, preventing the web container from failing during startup.
mischnic
added a commit
that referenced
this pull request
Aug 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #97358
ResolveResult::with_replaced_request_keyoverwrote theconditionsof every result key with the conditions of the replacement key (empty at all call sites). Those conditions are what distinguishes the results one request resolved to under different export conditions, so both themodule-syncand thedefault/requiretarget of the same subpath ended up under the sameRequestKey.That is lossy as soon as a request resolves in more than one
node_modulesdirectory: the results are merged through aRequestKey-keyed map, so one of the two targets is silently dropped. With a single candidate directoryResolveResult::alternativesshort-circuits and never builds that map, which is why this only showed up in nested layouts.In practice this broke
output: 'standalone'with the Turbopack production bundler for pnpm installs, whose virtual store hoists every package intonode_modules/.pnpm/node_modulesand thereby creates that second candidate directory:next-server.js.nft.jsonrecorded only@swc/helpers/cjs/_interop_require_default.cjs, while Node >= 22.12 resolves@swc/helpers/_/_interop_require_defaultto themodule-synctargetesm/_interop_require_default.js(@swc/helpers0.5.23 listsmodule-syncfirst). The copied.next/standalone/server.jsthen exited with MODULE_NOT_FOUND before listening.