Skip to content

Conversation

@lucasadrianof
Copy link

@lucasadrianof lucasadrianof commented Oct 27, 2025

Fixes #85416.

This is a follow up to #77662, where the Node.js middleware was fixed to also support reading (and duplicating) the body's request before being passed to the server action.

However, in that PR, an await was missed while calling the .finalize() method, which returns a Promise. That missing await would sometimes cause a bug, where the server action would be invoked before the body was completely cloned, causing the bug mentioned in the issue above.

@ijjk
Copy link
Member

ijjk commented Oct 27, 2025

Allow CI Workflow Run

  • approve CI run for commit: 545f208

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

@lucasadrianof lucasadrianof force-pushed the fix/await-middleware-nodejs-copy branch from 235ef84 to 545f208 Compare October 27, 2025 20:17
@lucasadrianof lucasadrianof marked this pull request as ready for review October 27, 2025 20:17
@ijjk
Copy link
Member

ijjk commented Oct 27, 2025

Failing test suites

Commit: 545f208 | About building and testing Next.js

pnpm test-dev-turbo test/e2e/app-dir/server-source-maps/server-source-maps-edge.test.ts (turbopack)

  • app-dir - server source maps edge runtime > logged errors have a sourcemapped stack with a codeframe (DD)
Expand output

● app-dir - server source maps edge runtime › logged errors have a sourcemapped stack with a codeframe

expect(received).toContain(expected) // indexOf

Expected substring: "
Error: rsc-error-log

  at logError (../app/rsc-error-log/page.js:2:17)
  at Page (../app/rsc-error-log/page.js:6:3)
    1 | function logError() {
  > 2 |   console.error(new Error('rsc-error-log'))
      |                 ^
    3 | }
    4 |
    5 | export default function Page() { {···
  }"
  Received string:    "Error: rsc-error-log
  at logError (../app/rsc-error-log/page.js:2:17)
  at Page (../app/rsc-error-log/page.js:6:3)
    1 | function logError() {
  > 2 |   console.error(new Error('rsc-error-log'))
      |                 ^
    3 | }
    4 |
    5 | export default function Page() { {···
  }
  "
  at Object.toContain (e2e/app-dir/server-source-maps/server-source-maps-edge.test.ts:30:69)

pnpm test-dev test/e2e/app-dir/no-double-tailwind-execution/no-double-tailwind-execution.test.ts

  • no-double-tailwind-execution > should run tailwind only once initially and per change (DD)
Expand output

● no-double-tailwind-execution › should run tailwind only once initially and per change

expect(received).toBe(expected) // Object.is equality

Expected: 3
Received: 2

  48 |     ].length
  49 |     if (isNextDev) {
> 50 |       expect(tailwindProcessingCount).toBe(3) // dev: initial + hmr + hmr (revert)
     |                                       ^
  51 |     } else {
  52 |       expect(tailwindProcessingCount).toBe(1) // build
  53 |     }

  at Object.toBe (e2e/app-dir/no-double-tailwind-execution/no-double-tailwind-execution.test.ts:50:39)

pnpm test-dev test/e2e/app-dir/fallback-shells/fallback-shells.test.ts

  • fallback-shells > without IO > should start and not postpone the response (DD)
Expand output

● fallback-shells › without IO › should start and not postpone the response

thrown: "Timed out waiting for the response of /without-io/world"

   8 |
   9 |   describe('without IO', () => {
> 10 |     it('should start and not postpone the response', async () => {
     |     ^
  11 |       const { browser, response } =
  12 |         await next.browserWithResponse('/without-io/world')
  13 |

  at it (e2e/app-dir/fallback-shells/fallback-shells.test.ts:10:5)
  at describe (e2e/app-dir/fallback-shells/fallback-shells.test.ts:9:3)
  at Object.describe (e2e/app-dir/fallback-shells/fallback-shells.test.ts:4:1)

pnpm test-dev test/e2e/app-dir/use-cache-custom-handler/use-cache-custom-handler.test.ts

  • use-cache-custom-handler > should use a modern custom cache handler if provided (DD)
Expand output

● use-cache-custom-handler › should use a modern custom cache handler if provided

expect(received).toEqual(expected) // deep equality

Expected: "2025-10-27T20:46:30.398Z"
Received: "2025-10-27T20:46:33.746Z"

  47 |     let data = await browser.elementById('data').text()
  48 |     expect(data).toMatch(isoDateRegExp)
> 49 |     expect(data).toEqual(initialData)
     |                  ^
  50 |
  51 |     // Now that a cache entry exists, we expect that getExpiration() is called
  52 |     // to compare the cache entries timestamp with the expiration of the

  at Object.toEqual (e2e/app-dir/use-cache-custom-handler/use-cache-custom-handler.test.ts:49:18)

pnpm test-start test/e2e/app-dir/segment-cache/dynamic-on-hover/dynamic-on-hover.test.ts

  • dynamic on hover > prefetches the dynamic data for a Link on hover (DD)
Expand output

● dynamic on hover › prefetches the dynamic data for a Link on hover

thrown: "Exceeded timeout of 120000 ms for a test.
Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout."

  12 |   }
  13 |
> 14 |   it('prefetches the dynamic data for a Link on hover', async () => {
     |   ^
  15 |     let act: ReturnType<typeof createRouterAct>
  16 |     const browser = await next.browser('/', {
  17 |       beforePageLoad(p: Playwright.Page) {

  at it (e2e/app-dir/segment-cache/dynamic-on-hover/dynamic-on-hover.test.ts:14:3)
  at Object.describe (e2e/app-dir/segment-cache/dynamic-on-hover/dynamic-on-hover.test.ts:5:1)

pnpm test-start test/e2e/app-dir/app-prefetch/prefetching.test.ts

  • app dir - prefetching > should show layout eagerly when prefetched with loading one level down (DD)
  • app dir - prefetching > should immediately render the loading state for a dynamic segment when fetched from higher up in the tree (DD)
Expand output

● app dir - prefetching › should show layout eagerly when prefetched with loading one level down

thrown: "Exceeded timeout of 120000 ms for a test.
Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout."

  26 |   })
  27 |
> 28 |   it('should show layout eagerly when prefetched with loading one level down', async () => {
     |   ^
  29 |     let act: ReturnType<typeof createRouterAct>
  30 |     const timeController = createTimeController()
  31 |     const browser = await next.browser('/', {

  at it (e2e/app-dir/app-prefetch/prefetching.test.ts:28:3)
  at Object.describe (e2e/app-dir/app-prefetch/prefetching.test.ts:11:1)

● app dir - prefetching › should immediately render the loading state for a dynamic segment when fetched from higher up in the tree

thrown: "Exceeded timeout of 120000 ms for a test.
Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout."

  302 |   })
  303 |
> 304 |   it('should immediately render the loading state for a dynamic segment when fetched from higher up in the tree', async () => {
      |   ^
  305 |     let act: ReturnType<typeof createRouterAct>
  306 |     const browser = await next.browser('/', {
  307 |       beforePageLoad(page) {

  at it (e2e/app-dir/app-prefetch/prefetching.test.ts:304:3)
  at Object.describe (e2e/app-dir/app-prefetch/prefetching.test.ts:11:1)

pnpm test-start-turbo test/e2e/app-dir/segment-cache/prefetch-layout-sharing/prefetch-layout-sharing.test.ts (turbopack)

  • layout sharing in non-static prefetches > full prefetches should omit layouts that were already prefetched with a full prefetch (DD)
  • layout sharing in non-static prefetches > navigations should omit layouts that were already prefetched with a full prefetch (DD)
  • layout sharing in non-static prefetches > segment-level prefetch config > does not unnecessarily use a runtime prefetch for sub-pages of runtime-prefetchable layouts (DD)
  • layout sharing in non-static prefetches > segment-level prefetch config > statically prefetches a fully-static page segment if all its runtime-prefetchable parents are available (DD)
  • layout sharing in non-static prefetches > segment-level prefetch config > uses a runtime prefetch for sub-pages of runtime-prefetchable layouts if requested (DD)
Expand output

● layout sharing in non-static prefetches › full prefetches should omit layouts that were already prefetched with a full prefetch

thrown: "Exceeded timeout of 120000 ms for a test.
Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout."

  119 |   })
  120 |
> 121 |   it('full prefetches should omit layouts that were already prefetched with a full prefetch', async () => {
      |   ^
  122 |     // Prefetches should re-use results from previous prefetches with the same fetch strategy.
  123 |
  124 |     let page: Playwright.Page

  at it (e2e/app-dir/segment-cache/prefetch-layout-sharing/prefetch-layout-sharing.test.ts:121:3)
  at Object.describe (e2e/app-dir/segment-cache/prefetch-layout-sharing/prefetch-layout-sharing.test.ts:6:1)

● layout sharing in non-static prefetches › navigations should omit layouts that were already prefetched with a full prefetch

thrown: "Exceeded timeout of 120000 ms for a test.
Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout."

  182 |   })
  183 |
> 184 |   it('navigations should omit layouts that were already prefetched with a full prefetch', async () => {
      |   ^
  185 |     // A navigation is mostly equivalent to a full prefetch, so it should re-use results from full prefetches.
  186 |
  187 |     let page: Playwright.Page

  at it (e2e/app-dir/segment-cache/prefetch-layout-sharing/prefetch-layout-sharing.test.ts:184:3)
  at Object.describe (e2e/app-dir/segment-cache/prefetch-layout-sharing/prefetch-layout-sharing.test.ts:6:1)

● layout sharing in non-static prefetches › segment-level prefetch config › does not unnecessarily use a runtime prefetch for sub-pages of runtime-prefetchable layouts

thrown: "Exceeded timeout of 120000 ms for a test.
Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout."

  614 |     }
  615 |
> 616 |     it('does not unnecessarily use a runtime prefetch for sub-pages of runtime-prefetchable layouts', async () => {
      |     ^
  617 |       let page: Playwright.Page
  618 |       const browser = await next.browser('/', {
  619 |         beforePageLoad(p: Playwright.Page) {

  at it (e2e/app-dir/segment-cache/prefetch-layout-sharing/prefetch-layout-sharing.test.ts:616:5)
  at describe (e2e/app-dir/segment-cache/prefetch-layout-sharing/prefetch-layout-sharing.test.ts:584:3)
  at Object.describe (e2e/app-dir/segment-cache/prefetch-layout-sharing/prefetch-layout-sharing.test.ts:6:1)

● layout sharing in non-static prefetches › segment-level prefetch config › statically prefetches a fully-static page segment if all its runtime-prefetchable parents are available

thrown: "Exceeded timeout of 120000 ms for a test.
Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout."

  647 |     })
  648 |
> 649 |     it('statically prefetches a fully-static page segment if all its runtime-prefetchable parents are available', async () => {
      |     ^
  650 |       let page: Playwright.Page
  651 |       const browser = await next.browser('/', {
  652 |         beforePageLoad(p: Playwright.Page) {

  at it (e2e/app-dir/segment-cache/prefetch-layout-sharing/prefetch-layout-sharing.test.ts:649:5)
  at describe (e2e/app-dir/segment-cache/prefetch-layout-sharing/prefetch-layout-sharing.test.ts:584:3)
  at Object.describe (e2e/app-dir/segment-cache/prefetch-layout-sharing/prefetch-layout-sharing.test.ts:6:1)

● layout sharing in non-static prefetches › segment-level prefetch config › uses a runtime prefetch for sub-pages of runtime-prefetchable layouts if requested

thrown: "Exceeded timeout of 120000 ms for a test.
Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout."

  691 |     })
  692 |
> 693 |     it('uses a runtime prefetch for sub-pages of runtime-prefetchable layouts if requested', async () => {
      |     ^
  694 |       let page: Playwright.Page
  695 |       const browser = await next.browser('/', {
  696 |         beforePageLoad(p: Playwright.Page) {

  at it (e2e/app-dir/segment-cache/prefetch-layout-sharing/prefetch-layout-sharing.test.ts:693:5)
  at describe (e2e/app-dir/segment-cache/prefetch-layout-sharing/prefetch-layout-sharing.test.ts:584:3)
  at Object.describe (e2e/app-dir/segment-cache/prefetch-layout-sharing/prefetch-layout-sharing.test.ts:6:1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Race condition with Node.js middleware body cloning

2 participants