mswjs / msw

Industry standard API mocking for JavaScript.
https://mswjs.io
MIT License
15.97k stars 519 forks source link

TypeError: Cannot assign to read only property 'Headers' of object '[object Object]' #2281

Closed kettanaito closed 2 months ago

kettanaito commented 2 months ago

Discussed in https://github.com/mswjs/msw/discussions/2271

Originally posted by **ch-liuzhide** September 10, 2024 I encountered the following error after updating the node_modules folder and then running the tests, ```bash TypeError: Cannot assign to read only property 'Headers' of object '[object Object]' 14 | describe('test/stream/client/xxx.tests.ts', () => { 15 | beforeAll(() => { > 16 | server.listen(); | ^ 17 | }); 18 | 19 | afterEach(() => { at recordRawFetchHeaders (node_modules/@mswjs/interceptors/lib/node/chunk-BMRE2LOX.js:157:3) at _ClientRequestInterceptor.setup (node_modules/@mswjs/interceptors/lib/node/chunk-BMRE2LOX.js:995:5) at _ClientRequestInterceptor.apply (node_modules/@mswjs/interceptors/lib/node/chunk-AABH3XLQ.js:75:10) at BatchInterceptor.setup (node_modules/@mswjs/interceptors/lib/node/chunk-PGTBKPWN.js:17:19) at BatchInterceptor.apply (node_modules/@mswjs/interceptors/lib/node/chunk-AABH3XLQ.js:75:10) at SetupServerApi.listen (node_modules/msw/lib/node/index.js:100:22) ``` Does anyone have similar mistakes? How should I deal with it?
kettanaito commented 2 months ago

Here's the function in question: https://github.com/mswjs/interceptors/blob/4227c6eb360f490f1448860c8cfb2ac2a9688225/src/interceptors/ClientRequest/utils/recordRawHeaders.ts.

We are defining properties in two places there:

Object.defineProperty(headers, kRawHeaders, {
Object.defineProperty(Headers, kRestorePatches, {

Perhaps one of them throws. What's interesting is that both Node.js v18 and v20 test suites are passing. This needs to be investigated by reproducing the issue and looking at the exact stack trace pointing to the built modules.

kettanaito commented 2 months ago

@ch-liuzhide hi 👋 Can you please share more info on this issue? What version of Node.js are you using? Is there a chance to publish a reproduction repo for this? That'd help a lot. Thanks.

ctklc commented 2 months ago

I can give more context @kettanaito. I run on Node v20.9.0. I cannot share the repo unfortunately. But here is the trace:

  at recordRawFetchHeaders (node_modules/@mswjs/interceptors/src/interceptors/ClientRequest/utils/recordRawHeaders.ts:137:3)
  at _ClientRequestInterceptor.setup (node_modules/@mswjs/interceptors/src/interceptors/ClientRequest/index.ts:112:5)
  at _ClientRequestInterceptor.apply (node_modules/@mswjs/interceptors/src/Interceptor.ts:130:10)
  at BatchInterceptor.setup (node_modules/@mswjs/interceptors/src/BatchInterceptor.ts:44:19)
  at BatchInterceptor.apply (node_modules/@mswjs/interceptors/src/Interceptor.ts:130:10)
  at SetupServerApi.listen (node_modules/msw/src/node/SetupServerCommonApi.ts:102:22)

So I assume here?

kettanaito commented 2 months ago

Thanks. I'd think so but that assigns on the Headers class. The original error says the code attempts to assign on X where Headers is a readonly property. That's the tricky part. I don't see such an assignment in the code.

SapiensAnatis commented 2 months ago

I'm getting this stack trace which sounds similar, although I'm not sure if it's the same issue.

For me it occurs whenever I attempt to make a fetch request from the server during SSR. I'm on Node v22.1.0.

TypeError: Cannot set property headers of #<_Request> which has only a getter
    at Object.construct (file:///C:/Users/jay0/Projects/Dawnshard/Website/node_modules/.pnpm/@mswjs+interceptors@0.35.0/node_modules/@mswjs/interceptors/lib/node/chunk-77GKBGPN.mjs:205:25)
    at Object.handleFetch (C:\Users\jay0\Projects\Dawnshard\Website\src\hooks.server.ts:40:18)

   -- snip --

I don't have a neat sourcemap like the one above, but in my node_modules folder, chunk-77GKBGPN.mjs:205 indicates it is from recordRawHeaders.ts and looks like this:

Request = new Proxy(Request, {
  construct(target, args, newTarget) {
    if (typeof args[1] === "object" && args[1].headers != null && args[1].headers instanceof Headers && Reflect.has(args[1].headers, kRawHeaders)) {
      args[1].headers = args[1].headers[kRawHeaders]; // <----------- this is line 205
    }
    const request = Reflect.construct(target, args, newTarget);
    if (typeof args[1] === "object" && args[1].headers != null) {
      defineRawHeaders(request.headers, inferRawHeaders(args[1].headers));
    }
    return request;
  }
});

I think that maps to here:
https://github.com/mswjs/interceptors/blob/v0.35.0/src/interceptors/ClientRequest/utils/recordRawHeaders.ts#L153

kettanaito commented 2 months ago

Root cause

This was causing the problem, the restoration line: https://github.com/mswjs/interceptors/blob/017469ed8cf59858ee02ec7826f4924ee7639fee/src/interceptors/ClientRequest/utils/recordRawHeaders.ts#L58. And that was caused by interceptors just reassigning global Headers: https://github.com/mswjs/interceptors/blob/017469ed8cf59858ee02ec7826f4924ee7639fee/src/interceptors/ClientRequest/utils/recordRawHeaders.ts#L73.

Fix is open at https://github.com/mswjs/interceptors/pull/638

kettanaito commented 2 months ago

Released: v2.4.7 🎉

This has been released in v2.4.7!

Make sure to always update to the latest version (npm i msw@latest) to get the newest features and bug fixes.


Predictable release automation by @ossjs/release.

ctklc commented 2 months ago

Unfortunately bug is still exist.

Now it seems in here

TypeError: Cannot redefine property: Request at Function.defineProperty ()

  at recordRawFetchHeaders (node_modules/@mswjs/interceptors/src/interceptors/ClientRequest/utils/recordRawHeaders.ts:157:10)
  at _ClientRequestInterceptor.setup (node_modules/@mswjs/interceptors/src/interceptors/ClientRequest/index.ts:112:5)
  at _ClientRequestInterceptor.apply (node_modules/@mswjs/interceptors/src/Interceptor.ts:130:10)
  at BatchInterceptor.setup (node_modules/@mswjs/interceptors/src/BatchInterceptor.ts:44:19)
  at BatchInterceptor.apply (node_modules/@mswjs/interceptors/src/Interceptor.ts:130:10)
  at SetupServerApi.listen (node_modules/msw/src/node/SetupServerCommonApi.ts:102:22)
kettanaito commented 2 months ago

@ctklc, what environment are you running in? Request can be redefined in Node.js, so I suspect you are using something like JSDOM, perhaps?

I'd need a reproduction repository to reopen this. If you can provide one, I'd be grateful.

ctklc commented 2 months ago

Ah yeah we are using JSDOM and again because of msw, we have polyfills to define TextEncoder and Request etc.

kettanaito commented 2 months ago

I'm afraid nothing I can do in that case. Here's the library's stance on supporting Jest/JSDOM. I've wasted way too many hours trying to patch holes on that sinking ship. It's in shared interest to put our energy elsewhere. You can start from migrating to happy-dom, which is a replacement for jsdom. It will likely fix your issue.