remix: Error: Expected a Response to be returned from queryRoute

What version of Remix are you using?

1.8.0

Steps to Reproduce

I’m downloading an image from S3 with a resource route and I get Error: Expected a Response to be returned from queryRoute.

This doesn’t happen in 1.7.6.

Expected Behavior

Get image

Actual Behavior

GET /assets/images/[AWSIMAGEKEY]?w=768&format=webp 500 - - 563.637 ms
The following error is a bug in Remix; please open an issue! https://github.com/remix-run/remix/issues/new
Error: Expected a Response to be returned from queryRoute
    at Object.invariant [as default] (/Users/.../node_modules/@remix-run/server-runtime/dist/invariant.js:18:11)
    at handleResourceRequestRR (/Users/.../node_modules/@remix-run/server-runtime/dist/server.js:267:25)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at requestHandler (/Users/.../node_modules/@remix-run/server-runtime/dist/server.js:46:18)
    at /Users/.../node_modules/@remix-run/express/dist/server.js:39:22

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 2
  • Comments: 19 (9 by maintainers)

Most upvoted comments

@brophdawg11 Here’s the response object we’re getting from our loader:

Response {
   size: 0,
   [Symbol(Body internals)]: {
     body: ReadableStream {
       _state: 'readable',
       _reader: undefined,
       _storedError: undefined,
       _disturbed: false,
       _readableStreamController: [ReadableStreamDefaultController]
     },
     type: null,
     size: null,
     boundary: null,
     disturbed: false,
     error: null
   },
   [Symbol(Response internals)]: {
     url: 'https://maps.googleapis.com/maps/api/place/autocomplete/json?input=a&key=<api-key>&components=country:us',
     status: 200,
     statusText: 'OK',
     headers: {
       'accept-ranges': 'none',
       'alt-svc': 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"',
       'cache-control': 'private, max-age=300',
       connection: 'close',
       'content-type': 'application/json; charset=UTF-8',
       date: 'Tue, 06 Dec 2022 23:05:13 GMT',
       expires: 'Tue, 06 Dec 2022 23:05:13 GMT',
       server: 'scaffolding on HTTPServer2',
       'server-timing': 'gfet4t7; dur=21',
       'transfer-encoding': 'chunked',
       vary: 'Accept-Language,Accept-Encoding',
       'x-frame-options': 'SAMEORIGIN',
       'x-xss-protection': '0'
     },
     counter: 0,
     highWaterMark: 16384
   }
 }

Yeah this boils down to the CJS Response class being used by your node server versus the ESM Response class being returned from the loader in your compiled bundle. We just released the fix for this in a prerelease (1.8.2-pre.0) if folks want to give that a shot and confirm it fixes the issues!

All good! It works now.

Thank you! This simple reproduction will be a big help. My initial hunch is that it’s something with our instanceof Response check and we may have multiple Response classes defined. Planning to dig in today 👍

@brophdawg11 1.8.2-pre.0 works! it fixed the issue. Thanks Matt.

@brophdawg11 downgrading to 1.7.6 was not working for us either. We changed calls from fetch to axios and passed response.data between calls instead of the whole response object. This resolved the issue.

Yeah this boils down to the CJS Response class being used by your node server versus the ESM Response class being returned from the loader in your compiled bundle. We just released the fix for this in a prerelease (1.8.2-pre.0) if folks want to give that a shot and confirm it fixes the issues!

Oh, and in my case I’m simply returning fetch from my loader. Interestingly whether I use global fetch or the one from @remix-run/web-fetch, the response is a Remix Response.

So it appears that somehow the global Node Response isn’t getting overridden by the Remix Response for handleResourceRequestRR 🤷‍♂️

Heya, I just bumped into this. I can confirm that it’s response instances that are the problem. I added this console.log to handleResourceRequestRR:

    console.log({
      globalResponse: Response,
      remixResponse: webFetch.Response,
      responseIsGlobal: response instanceof Response,
      responseIsRemix: response instanceof webFetch.Response,
      responsesAreSame: Response === webFetch.Response,
    });

Here’s what I got:

{
  globalResponse: [class NodeResponse extends Response],
  remixResponse: [class Response extends Body],
  responseIsGlobal: false,
  responseIsRemix: true,
  responsesAreSame: false
}

instanceof checks are always going to be problematic I think. People may want to use node-fetch for whatever reason (maybe a client library they’re using uses it) and they’ll be surprised/disappointed if this doesn’t “just work” because they will then have to figure out how to convert the node-fetch response to a web fetch response.

Any instanceof will need to change to a isResponseLike call or something.