[GH-ISSUE #241] RSC serialization fails with null-prototype params from matchPattern() #59

Closed
opened 2026-05-06 12:36:54 +02:00 by BreizhHardware · 0 comments

Originally created by @gagipro on GitHub (Mar 3, 2026).
Original GitHub issue: https://github.com/cloudflare/vinext/issues/241

Bug Description

When a Next.js app uses dynamic route params (e.g. /dashboards/[id]), the RSC serializer throws an error because matchPattern() internally uses Object.create(null), producing objects with a null prototype.

The current code in app-dev-server.ts does:

const asyncParams = Object.assign(Promise.resolve(params), params);

This creates a Promise-like object that the RSC serializer detects as a thenable. It resolves the thenable, gets back the original null-prototype object, and throws because it cannot serialize objects without Object.prototype.

Root Cause

Two issues compounding:

  1. matchPattern() returns Object.create(null) objects (null prototype)
  2. Object.assign(Promise.resolve(params), params) produces a thenable that the RSC serializer resolves back to the original null-prototype object

Fix

Replace with a plain spread that normalizes the prototype:

function makeThenableParams(obj) {
  return { ...obj };
}

Since await plainObject returns the object itself in JS, a .then() method is not needed — React 19 / Next.js 16 components can await plain objects.

Files Changed

File What changed
packages/vinext/src/server/app-dev-server.ts Added makeThenableParams(), replaced 6 occurrences of Object.assign(Promise.resolve(...), ...). Changed credentials: "include" to credentials: "same-origin" on 4 fetch calls to match Next.js behavior.
packages/vinext/src/shims/metadata.tsx Same makeThenableParams() pattern, replaced 3 occurrences in resolveModuleViewport and resolveModuleMetadata.
packages/vinext/src/index.ts Added findReactServerPackages() to auto-exclude packages with react-server export condition from RSC optimizeDeps (prevents pre-bundling issues).

Reproduction

  • Use Vinext v0.0.18 with a Next.js app that has dynamic routes (/dashboards/[id])
  • Run in production mode (NODE_ENV=production)
  • Navigate to any dynamic route → 500 error from RSC serialization

Environment

  • Vinext: v0.0.18
  • Next.js: 16
  • React: 19
  • Node: 24
Originally created by @gagipro on GitHub (Mar 3, 2026). Original GitHub issue: https://github.com/cloudflare/vinext/issues/241 ## Bug Description When a Next.js app uses dynamic route params (e.g. `/dashboards/[id]`), the RSC serializer throws an error because `matchPattern()` internally uses `Object.create(null)`, producing objects with a **null prototype**. The current code in `app-dev-server.ts` does: ```js const asyncParams = Object.assign(Promise.resolve(params), params); ``` This creates a Promise-like object that the RSC serializer detects as a thenable. It resolves the thenable, gets back the **original null-prototype object**, and throws because it cannot serialize objects without `Object.prototype`. ## Root Cause Two issues compounding: 1. `matchPattern()` returns `Object.create(null)` objects (null prototype) 2. `Object.assign(Promise.resolve(params), params)` produces a thenable that the RSC serializer resolves back to the original null-prototype object ## Fix Replace with a plain spread that normalizes the prototype: ```js function makeThenableParams(obj) { return { ...obj }; } ``` Since `await plainObject` returns the object itself in JS, a `.then()` method is not needed — React 19 / Next.js 16 components can `await` plain objects. ## Files Changed | File | What changed | |------|-------------| | `packages/vinext/src/server/app-dev-server.ts` | Added `makeThenableParams()`, replaced 6 occurrences of `Object.assign(Promise.resolve(...), ...)`. Changed `credentials: "include"` to `credentials: "same-origin"` on 4 fetch calls to match Next.js behavior. | | `packages/vinext/src/shims/metadata.tsx` | Same `makeThenableParams()` pattern, replaced 3 occurrences in `resolveModuleViewport` and `resolveModuleMetadata`. | | `packages/vinext/src/index.ts` | Added `findReactServerPackages()` to auto-exclude packages with `react-server` export condition from RSC `optimizeDeps` (prevents pre-bundling issues). | ## Reproduction - Use Vinext v0.0.18 with a Next.js app that has dynamic routes (`/dashboards/[id]`) - Run in production mode (`NODE_ENV=production`) - Navigate to any dynamic route → 500 error from RSC serialization ## Environment - Vinext: v0.0.18 - Next.js: 16 - React: 19 - Node: 24
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#59
No description provided.