clerk/javascript

[beta] Error in Vercel from `Request` global mismatch

itsMapleLeaf opened this issue · 2 comments

Preliminary Checks

Reproduction

https://github.com/itsMapleLeaf/clerk-beta-remix-vercel-bug

Publishable key

pk_test_ZnJhbmstcHVnLTkwLmNsZXJrLmFjY291bnRzLmRldiQ

Description

Steps to reproduce:

git clone https://github.com/itsMapleLeaf/clerk-beta-remix-vercel-bug
cd clerk-beta-remix-vercel-bug
npx vercel deploy

Expected behavior:

A working Vercel deployment

Actual behavior:

The app crashes at runtime with the following error:

TypeError: Failed to parse URL from [object Request]
    at new Request (node:internal/deps/undici/undici:5855:19)
    at new ClerkRequest (/var/task/node_modules/@clerk/backend/dist/internal.js:2213:5)
    ... 6 lines matching cause stack trace ...
    at runHandler (/var/task/node_modules/@remix-run/server-runtime/node_modules/@remix-run/router/dist/router.cjs.js:4110:26)
    at callLoaderOrAction (/var/task/node_modules/@remix-run/server-runtime/node_modules/@remix-run/router/dist/router.cjs.js:4166:22) {
  [cause]: TypeError: Invalid URL
      at new URL (node:internal/url:775:36)
      at new Request (node:internal/deps/undici/undici:5853:25)
      at new ClerkRequest (/var/task/node_modules/@clerk/backend/dist/internal.js:2213:5)
      at createClerkRequest (/var/task/node_modules/@clerk/backend/dist/internal.js:2247:54)
      at loadOptions (/var/task/node_modules/@clerk/remix/dist/ssr/loadOptions.js:34:63)
      at rootAuthLoader (/var/task/node_modules/@clerk/remix/dist/ssr/rootAuthLoader.js:33:60)
      at loader$1 (file:///var/task/build/server/nodejs-eyJydW50aW1lIjoibm9kZWpzIn0/index.js:227:28)
      at Object.callRouteLoaderRR (/var/task/node_modules/@remix-run/server-runtime/dist/data.js:52:22)
      at commonRoute.loader (/var/task/node_modules/@remix-run/server-runtime/dist/routes.js:54:20)
      at runHandler (/var/task/node_modules/@remix-run/server-runtime/node_modules/@remix-run/router/dist/router.cjs.js:4110:26) {
    code: 'ERR_INVALID_URL',
    input: '[object Request]'
  }
}

The reproduction here is specifically in the case of Vercel + Remix, but I think the root of the issue is the [ClerkRequest](https://github.com/clerk/javascript/blob/178974f9fc562410f8bcc6f83da0484cac961211/packages/backend/src/tokens/clerkRequest.ts#L7) class.

Here's what happens:

  1. @clerk/backend is imported, and this class is defined, extending the Request global from Node.js/undici
  2. Vercel (or any other naïve server file) calls [installGlobals()](https://github.com/vercel/vercel/blob/326fe0f0e647a80ef504ca4035c6bc3c206f43d6/packages/remix/defaults/server-node.mjs#L7) and replaces the global [Request](https://github.com/vercel/vercel/blob/326fe0f0e647a80ef504ca4035c6bc3c206f43d6/packages/remix/defaults/server-node.mjs#L7) with the one from Remix
  3. At runtime, a Remix Request gets passed into a ClerkRequest
  4. Via super(), ClerkRequest passes the Remix Request to the Node.js Request
  5. Node.js doesn't recognize the request instance, so it blindly tries to stringify it and parse it as URL
  6. 💥

If possible, the code should change to not rely on a class whose identity is locked at definition time.

Environment

System:
    OS: Windows 11 10.0.22631
    CPU: (16) x64 AMD Ryzen 7 2700X Eight-Core Processor         
    Memory: 12.44 GB / 31.94 GB
  Binaries:
    Node: 20.11.1 - ~\scoop\apps\nvm\current\nodejs\nodejs\node.EXE
    Yarn: 1.22.19 - ~\scoop\apps\nvm\current\nodejs\nodejs\yarn.CMD
    npm: 10.2.4 - ~\scoop\apps\nvm\current\nodejs\nodejs\npm.CMD
    pnpm: 8.15.4 - ~\scoop\apps\nvm\current\nodejs\nodejs\pnpm.CMD
    bun: 1.1.0 - ~\.bun\bin\bun.EXE
  Browsers:
    Edge: Chromium (122.0.2365.92)
    Internet Explorer: 11.0.22621.1
  npmPackages:
    @ariakit/react: ^0.4.4 => 0.4.5 
    @biomejs/biome: 1.6.3 => 1.6.3 
    @clerk/remix: 4.0.0-beta.39 => 4.0.0-beta.39 
    @clerk/themes: 2.0.0-beta.8 => 2.0.0-beta.8 
    @floating-ui/react: 0.26.9 => 0.26.9 
    @floating-ui/react-dom: 2.0.8 => 2.0.8 
    @fontsource-variable/nunito: 5.0.18 => 5.0.18 
    @remix-run/dev: ^2.8.1 => 2.8.1 
    @remix-run/node: ^2.8.1 => 2.8.1 
    @remix-run/react: ^2.8.1 => 2.8.1 
    @remix-run/serve: ^2.8.1 => 2.8.1 
    @tailwindcss/container-queries: 0.1.1 => 0.1.1 
    @total-typescript/ts-reset: 0.5.1 => 0.5.1 
    @types/lodash-es: 4.17.12 => 4.17.12 
    @types/node: ^20.12.2 => 20.12.2 
    @types/react: ^18.2.70 => 18.2.73 
    @types/react-dom: ^18.2.22 => 18.2.23 
    @vercel/remix: 2.8.1 => 2.8.1 
    colorjs.io: 0.5.0 => 0.5.0 
    convex: 1.10.0 => 1.10.0 
    convex-helpers: 0.1.27 => 0.1.27 
    date-fns: 3.6.0 => 3.6.0 
    dotenv-cli: 7.4.1 => 7.4.1 
    hash-wasm: 4.11.0 => 4.11.0 
    isbot: ^5.1.2 => 5.1.3 
    lodash-es: 4.17.21 => 4.17.21 
    lucide-react: 0.363.0 => 0.363.0 
    npm-run-all: 4.1.5 => 4.1.5 
    prettier: 3.2.5 => 3.2.5 
    prettier-plugin-jsdoc: 1.3.0 => 1.3.0 
    prettier-plugin-tailwindcss: 0.5.12 => 0.5.12 
    random-word-slugs: 0.1.7 => 0.1.7 
    react: 19.0.0-canary-95e6f032c-20240401 => 19.0.0-canary-95e6f032c-20240401 
    react-dom: 19.0.0-canary-95e6f032c-20240401 => 19.0.0-canary-95e6f032c-20240401 
    react-textarea-autosize: 8.5.3 => 8.5.3 
    react-virtuoso: 4.7.4 => 4.7.4 
    remix-flat-routes: 0.6.4 => 0.6.4 
    remix-routes: 1.7.2 => 1.7.2 
    rollup-plugin-visualizer: 5.12.0 => 5.12.0 
    tailwind-merge: 2.2.2 => 2.2.2 
    tailwindcss: 3.4.1 => 3.4.1 
    tailwindcss-animate: ^1.0.7 => 1.0.7 
    ts-dedent: 2.2.0 => 2.2.0 
    typescript: 5.4.3 => 5.4.3 
    vite: ^5.2.6 => 5.2.7 
    vite-plugin-inspect: 0.8.3 => 0.8.3 
    zod: 3.22.4 => 3.22.4

Hi, thanks for the detailed issue! We're collecting all Beta feedback in this GitHub discussion, so please repost your issue there. Thanks!

BTW, in order to achieve the expected functionality, I thoroughly reviewed the source code of both clerk/node-sdk and clerk/express. Through this process, I discovered that we need to transform the IncomingMessage to a Request object instance, as in the authenticateRequest. Eventually, following this approach I resolved the issue successfully.