msw: Request stuck in pending state / promise never resolves

Environment

Name Version
msw 0.35.0
node 14.17.6
OS codesandbox & ubuntu 20.04

Request handlers

// Example of declaration. Provide your code here.
import { setupServer } from 'msw/node'
import { rest } from 'msw'

const server = setupServer()

server.listen({
  onUnhandledRequest: "bypass"
})

stripe.customers.update("customer_id", {
  name: "stephan",
  email: "stephan@example.com"
}).finally(() => {
  // you'll never come here
});

Actual request

stripe.customers.update("customer_id", {
  name: "stephan",
  email: "stephan@example.com"
}).finally(() => {
  // you'll never come here
});

Current behavior

Request is made, Stripe shows that there was a successful response, but the promise never resolves.

Expected behavior

Request should resolve

Screenshots

codesandbox

https://codesandbox.io/s/spring-dawn-3cht3?file=/pages/api/stripe.js

terminal

https://user-images.githubusercontent.com/1196524/137531832-6b5fb6c8-e4f5-4c0d-9a2f-f16f1698f48c.mp4

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 2
  • Comments: 15 (7 by maintainers)

Most upvoted comments

I’ve implemented the fix in https://github.com/mswjs/interceptors/pull/165 but currently experience a regression in one of the tests. Once it’s resolved, the fix will be merged.

It seems I’ve finally found a way to properly clone the IncomingMessage by piping it to a PassThrough stream in order to read its “data” twice: for the internal “response” event and for the arbitrary response event listeners that the request client (or developer) may attach.

The fix will propagate with the next update of the @mswjs/interceptors library. We’re currently working on a few bug fixes we’d like to include in that next release. I’d expect it to be released this month.

If I understood correctly, this has been fixed in the dependency. Is there an eta on when a new msw will be published?

Root cause

The following response listener is the root cause of why the request never resolves:

https://github.com/mswjs/interceptors/blob/adc15848cb43e691dacf052792cb386bcad881c3/src/interceptors/ClientRequest/createClientRequestOverride.ts#L293-L300

It reads the IncomingMessage body and, once that’s done, any other listeners on res.on('data') will never get that event emitted. Here’s where Stripe attempts to get the response and parse it to JSON:

https://github.com/stripe/stripe-node/blob/7dd6c7cf98f8a5fab5d551d70aad4cf2e844d0db/lib/net/NodeHttpClient.js#L107-L122

This entire promise never resolves because the this._res.once('data') is never called once interceptors extract the response body via getIncomingMessageBody. In particular, because this function removes all listeners on the response, including those attached by Stripe:

https://github.com/mswjs/interceptors/blob/adc15848cb43e691dacf052792cb386bcad881c3/src/interceptors/ClientRequest/utils/getIncomingMessageBody.ts#L22

Removing this line makes the scenario pass.

That’s rather strange, as we clone the incoming message to prevent this very issue from happening.