thedevstockgirl
thedevstockgirl8mo ago

Running into some cryptic error. Help needed.

I am running into some error. This is on React 19, Nextjs 15 and all latest Convex. Has anyone seen this?
No description
54 Replies
erquhart
erquhart8mo ago
Any chance you're using Cloudflare? This React bug mentions Astro specifically but looks very similar: https://github.com/facebook/react/issues/31827 Hmm might not even be cloudflare specific, I think the error message just came from a cloudflare worker that was doing ssr maybe This has popped up in a few other places with different tooling. MessageChannel is a Node API used for SSR, so this error is happening in a non-node context. Another maybe related issue here that does involve Next 15: https://github.com/resend/react-email/issues/1630 This comment from a Vercel team member seems to hit the root of the issue: https://github.com/vercel/next.js/issues/71865#issuecomment-2444377726
thedevstockgirl
thedevstockgirlOP8mo ago
Thank you. Looking into all the links you shared now.
lee
lee8mo ago
interesting that you're getting the same error from next (presumably in a vercel environment) and from the convex environment. neither of them implement MessageChannel. any idea why you might be importing "react-dom/server"?
thedevstockgirl
thedevstockgirlOP8mo ago
Ah. Yes. That's interesting. @erquhart , you saved the day. After much debugging, uninstalling and re-installing, react-email was indeed the culprit. Things worked once I commented out, and used pure html.
No description
thedevstockgirl
thedevstockgirlOP8mo ago
I initially tried jsx-email, but that had a bun of node things that made convex unhappy.
lee
lee8mo ago
hmm i think we want "react-email" to work. we'll look into it. can you share the exact version of react-email and react-dom and other packages that may be relevant (npm ls) 🙏
thedevstockgirl
thedevstockgirlOP8mo ago
Ah. It was not just react-email. Even after I removed react email, attempting to pass anything to the react prop of resend, triggered the same error. Just spent the last hour converting all to pure html with the help of claude. Then using the html prop instead with getSomeTemplate, which returns a html string. No errors. But If I pass the same html to the react prop, it shows the same error I just updated all our packages. So the latest of all relevant package. The only react in the api folder is from resend and react-email. So its def something with react. Once I got rid of that, all works now
thedevstockgirl
thedevstockgirlOP8mo ago
For example, the below does not work
No description
erquhart
erquhart8mo ago
Sounds like react 19 specifically
thedevstockgirl
thedevstockgirlOP8mo ago
Yes
thedevstockgirl
thedevstockgirlOP8mo ago
This works:
No description
thedevstockgirl
thedevstockgirlOP8mo ago
@erquhart Yes. React 19.
Eva
Eva7mo ago
Hi! I just ran into this exact issue tonight while trying to install and use react-email. Was there a solution identified?
"react": "^19.0.0",
"react-dom": "^19.0.0",
"@react-email/components": "0.0.32",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"@react-email/components": "0.0.32",
erquhart
erquhart7mo ago
Yeah, not using react. There’s a non-working and working example above
Eva
Eva7mo ago
RIP I love the simplicity of Convex Auth but might need to switch to Clerk
David Alonso
David Alonso7mo ago
Just want to flag that we're having the same issue here, going for pure html for now, but would love to get updates if React 19 works at some point
ballingt
ballingt7mo ago
Thanks for flagging, how would you describe the issue @David Alonso, "React emails don't work with React 19 in Convex?" Sounds like it's to do with some Node.js specific APIs? Sounds like we need to prioritize this, but I don't know what the issue is yet. If it's a general runtime API we can add it, but if the issue is that React Email now only supports Node.js for React 19 that's something we need to open an issue about
ballingt
ballingt7mo ago
GitHub
Error: A Node.js API is used (MessageChannel) which is not supporte...
Describe the Bug Using React 19 + Next.js 15 I want to use @react-email/render on edge runtime. However, I got this error: unhandledRejection: Error: A Node.js API is used (MessageChannel) which is...
David Alonso
David Alonso7mo ago
yeah that's how I'd describe it but only based on what solved it for me, which was downgrading to react 18.3 (which is what the auth template uses). I briefly tried the pure HTML approach but that didn't seem to work for me (i might have missed something)
erquhart
erquhart5mo ago
So two issues and solutions: 1. MessageChannel not supported: use a node action. 2. reactDOMServer.default.renderToPipeableStream is undefined: Add @react-email/components (or /render, whichever you import render from) to node.externalPackages in convex.json So the status is "React emails work with React 19 in Convex, but only for Node actions". It is possible to use the Convex runtime, but the @react-email/render browser module is attempting to run reactDOMServer.renderToReadableStream when the actual import shape is reactDOMServer.default.renderToReadableStream. Works when patched. Going to see if there's an elegant solution, otherwise might make a patch-package for this. Here's the patch steps for anyone that wants this working in Convex runtime (patch below): - Make sure you're using @react-email/render 1.0.5 (or @react-email/components 0.0.35, which exports render from the same version) - Add the file above to a patches/ directory alongside your package.json (repo or project root) - Set up patch package to apply: https://www.npmjs.com/package/patch-package /patches/@react-email+render+1.0.5.patch
diff --git a/node_modules/@react-email/render/dist/browser/index.mjs b/node_modules/@react-email/render/dist/browser/index.mjs
index 0d63ab6..74e9b0d 100644
--- a/node_modules/@react-email/render/dist/browser/index.mjs
+++ b/node_modules/@react-email/render/dist/browser/index.mjs
@@ -155,9 +155,9 @@ var render = (element, options) => __async(void 0, null, function* () {
const suspendedElement = /* @__PURE__ */ jsx(Suspense, { children: element });
const reactDOMServer = yield import("react-dom/server");
let html2;
- if (Object.hasOwn(reactDOMServer, "renderToReadableStream")) {
+ if (Object.hasOwn(reactDOMServer.default, "renderToReadableStream")) {
html2 = yield readStream(
- yield reactDOMServer.renderToReadableStream(suspendedElement)
+ yield reactDOMServer.default.renderToReadableStream(suspendedElement)
);
} else {
yield new Promise((resolve, reject) => {
diff --git a/node_modules/@react-email/render/dist/browser/index.mjs b/node_modules/@react-email/render/dist/browser/index.mjs
index 0d63ab6..74e9b0d 100644
--- a/node_modules/@react-email/render/dist/browser/index.mjs
+++ b/node_modules/@react-email/render/dist/browser/index.mjs
@@ -155,9 +155,9 @@ var render = (element, options) => __async(void 0, null, function* () {
const suspendedElement = /* @__PURE__ */ jsx(Suspense, { children: element });
const reactDOMServer = yield import("react-dom/server");
let html2;
- if (Object.hasOwn(reactDOMServer, "renderToReadableStream")) {
+ if (Object.hasOwn(reactDOMServer.default, "renderToReadableStream")) {
html2 = yield readStream(
- yield reactDOMServer.renderToReadableStream(suspendedElement)
+ yield reactDOMServer.default.renderToReadableStream(suspendedElement)
);
} else {
yield new Promise((resolve, reject) => {
David Alonso
David Alonso5mo ago
@Michael
erquhart
erquhart5mo ago
Forgot to add, in Convex runtime I think you still need to polyfill MessageChannel, just put the MessageChannel mock below in a file and import it first in whatever file your email rendering is happening in. You may be able to get away with a more minimal polyfill but this one works.
// polyfill MessageChannel without using node:events
if (typeof MessageChannel === "undefined") {
class MockMessagePort {
onmessage: ((ev: MessageEvent) => void) | undefined;
onmessageerror: ((ev: MessageEvent) => void) | undefined;

close() {}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
postMessage(_message: unknown, _transfer: Transferable[] = []) {}
start() {}
addEventListener() {}
removeEventListener() {}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
dispatchEvent(_event: Event): boolean {
return false;
}
}

class MockMessageChannel {
port1: MockMessagePort;
port2: MockMessagePort;

constructor() {
this.port1 = new MockMessagePort();
this.port2 = new MockMessagePort();
}
}

globalThis.MessageChannel =
MockMessageChannel as unknown as typeof MessageChannel;
}
// polyfill MessageChannel without using node:events
if (typeof MessageChannel === "undefined") {
class MockMessagePort {
onmessage: ((ev: MessageEvent) => void) | undefined;
onmessageerror: ((ev: MessageEvent) => void) | undefined;

close() {}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
postMessage(_message: unknown, _transfer: Transferable[] = []) {}
start() {}
addEventListener() {}
removeEventListener() {}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
dispatchEvent(_event: Event): boolean {
return false;
}
}

class MockMessageChannel {
port1: MockMessagePort;
port2: MockMessagePort;

constructor() {
this.port1 = new MockMessagePort();
this.port2 = new MockMessagePort();
}
}

globalThis.MessageChannel =
MockMessageChannel as unknown as typeof MessageChannel;
}
Galactus
Galactus4mo ago
Super helpful! I'm now using "@react-email/render": "1.1.0" and having the same issue - Do i need to set my version to 1.0.5 to apply the patch? And is this something that will end up getting fixed or will I need the patch forever? After doing some more reading - is there a recommended way to use @react-email/components with resend + convex-auth? Would love to get that set up in my new app I was using the convex auth example repo - but it didn't have anything about this issue 😕 Just saw your response today about a PR for patching it 🙂 I'll keep my eyes peeled for that and save that work for later 👍
erquhart
erquhart4mo ago
Yeah it's complicated. There's an effort to provide an edge-light distro that would remove the need for a polyfill: https://github.com/resend/react-email/pull/2222 The default export issue has a PR open to but I'm having trouble finding it. At any rate, if you want to get this working I'd pin to 1.0.5 for now and use the fixes above. Otherwise hopefully fixes get in place soon.
Galactus
Galactus4mo ago
Thanks for the context! We probably won’t go live with the product for another 6 weeks so I’ll wait to see if it gets fixed 😀😀
Yared Yilma
Yared Yilma4mo ago
Have you by any chance created a working example for your better-auth + Convex implementation that shows how to send verification and password reset emails using Resend and React Email? I applied the patch following your guide correctly, but I am still getting a "MessageChannel is not defined" error.
erquhart
erquhart4mo ago
Yessir: https://github.com/erquhart/convex-better-auth/tree/main/examples/next For anyone that's still seeing the MessageChannel undefined issue after adding the polyfill: the polyfill must be imported before the other imports. The polyfill can't be inlined above the imports, it will still execute after the imports.
Jeremy
Jeremy4mo ago
I ran that patch and I'm getting this error
Hit error while running `auth:signIn`:
Error: [Request ID: e2fcebe8dac81e7d] Server Error
Uncaught TypeError: reactDOMServer.renderToPipeableStream is not a function
at __spreadValues2.selectors (../../node_modules/.pnpm/@react-email+render@1.0.5_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/@react-email/render/dist/browser/index.mjs:164:21)
at new Promise (<anonymous>)
at <anonymous> (../../node_modules/.pnpm/@react-email+render@1.0.5_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/@react-email/render/dist/browser/index.mjs:163:6)
at next [as next] (<anonymous>)
at fulfilled (../../node_modules/.pnpm/@react-email+render@1.0.5_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/@react-email/render/dist/browser/index.mjs:24:18)

at new Promise (<anonymous>)
at next [as next] (<anonymous>)
at async proxyAuthActionToConvex (../../../src/nextjs/server/proxy.ts:72:15)
at async (../../../src/nextjs/server/index.tsx:201:13) {

}
Hit error while running `auth:signIn`:
Error: [Request ID: e2fcebe8dac81e7d] Server Error
Uncaught TypeError: reactDOMServer.renderToPipeableStream is not a function
at __spreadValues2.selectors (../../node_modules/.pnpm/@react-email+render@1.0.5_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/@react-email/render/dist/browser/index.mjs:164:21)
at new Promise (<anonymous>)
at <anonymous> (../../node_modules/.pnpm/@react-email+render@1.0.5_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/@react-email/render/dist/browser/index.mjs:163:6)
at next [as next] (<anonymous>)
at fulfilled (../../node_modules/.pnpm/@react-email+render@1.0.5_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/@react-email/render/dist/browser/index.mjs:24:18)

at new Promise (<anonymous>)
at next [as next] (<anonymous>)
at async proxyAuthActionToConvex (../../../src/nextjs/server/proxy.ts:72:15)
at async (../../../src/nextjs/server/index.tsx:201:13) {

}
@erquhart
erquhart
erquhart4mo ago
Ah, I don't think patch-package works with pnpm
erquhart
erquhart4mo ago
pnpm supports patching natively: https://pnpm.io/cli/patch
pnpm patch <pkg> | pnpm
Prepare a package for patching (inspired by a similar command in Yarn).
erquhart
erquhart4mo ago
I haven't used it, but if you follow their patching instructions you can try manually using the patch I shared above to make the changes.
Jeremy
Jeremy4mo ago
I tried to remove pnpm from the project. and for some reason it's still trying to use the pnpm package
erquhart
erquhart4mo ago
If you want to drop pnpm, deleting node_modules and pnpm-lock.yaml should do it
Jeremy
Jeremy4mo ago
I removed every reference of pnpm from the project and did a completely fresh npm install and it is still using pnpm for that function Convex runtime wasn't running 🤦‍♂️
erquhart
erquhart4mo ago
ah that'll do it working now?
Eva
Eva3mo ago
Following up here since I just ran into the same issue with Better Auth, Resend, React Email, and Convex (MessageChannel is not defined). This is what fixed it for me: 1. Add convex.json at the root and specify @react-email/render as an external package:
{
"node": {
"externalPackages": ["@react-email/render"]
}
}
{
"node": {
"externalPackages": ["@react-email/render"]
}
}
2. Add an override in package.json for @react-email/render. The latest version has fixed the renderToPipeableStream issue described above, however, the latest versions of react-email and @react-email/components don't yet rely on up-to-date @react-email/render... so you have to override it manually:
"pnpm": {
"overrides": {
"@react-email/render": "1.1.2"
}
}
"pnpm": {
"overrides": {
"@react-email/render": "1.1.2"
}
}
3. Add the MessageChannel polyfill as described by @erquhart here: https://github.com/resend/react-email/issues/1630#issuecomment-2773421899 (I created a polyfills.ts file within convex/ and then added import "./pollyfills" to the top of convex/auth.ts I think that's it! Hopefully we can remove the polyfill and override at some point in the future, but at least no package patch is required now.
GitHub
Error: A Node.js API is used (MessageChannel) which is not supporte...
Describe the Bug Using React 19 + Next.js 15 I want to use @react-email/render on edge runtime. However, I got this error: unhandledRejection: Error: A Node.js API is used (MessageChannel) which is...
Roberto
Roberto3mo ago
This has worked for me. Thank you @Eva!
erquhart
erquhart3mo ago
Much better than using patch-package, thanks for the update Update: only the polyfill is needed if you're on the latest version of the @react-email/* libs (step 3 from Eva's last comment), no convex.json or dependency overrides required. The polyfill will unfortunately be required indefinitely, the fix is currently at a standstill: https://github.com/resend/react-email/pull/2225
vaverix
vaverix2mo ago
Thank you so much for the fix, I was trying to debug it for hours... It works now!
Zack07
Zack072mo ago
Hi everyone. Im in need of help. I have inserted this code
if (typeof MessageChannel === "undefined") {
class MockMessagePort {
onmessage: ((ev: MessageEvent) => void) | undefined;
onmessageerror: ((ev: MessageEvent) => void) | undefined;

close() {}
postMessage(_message: unknown, _transfer: Transferable[] = []) {}
start() {}
addEventListener() {}
removeEventListener() {}
dispatchEvent(_event: Event): boolean {
return false;
}
}

class MockMessageChannel {
port1: MockMessagePort;
port2: MockMessagePort;

constructor() {
this.port1 = new MockMessagePort();
this.port2 = new MockMessagePort();
}
}

globalThis.MessageChannel =
MockMessageChannel as unknown as typeof MessageChannel;
}
if (typeof MessageChannel === "undefined") {
class MockMessagePort {
onmessage: ((ev: MessageEvent) => void) | undefined;
onmessageerror: ((ev: MessageEvent) => void) | undefined;

close() {}
postMessage(_message: unknown, _transfer: Transferable[] = []) {}
start() {}
addEventListener() {}
removeEventListener() {}
dispatchEvent(_event: Event): boolean {
return false;
}
}

class MockMessageChannel {
port1: MockMessagePort;
port2: MockMessagePort;

constructor() {
this.port1 = new MockMessagePort();
this.port2 = new MockMessagePort();
}
}

globalThis.MessageChannel =
MockMessageChannel as unknown as typeof MessageChannel;
}
in the polyfills.ts last week and then I imported polyfills.ts in convex/auth.ts. It was working well until today. I got an error in convex logs saying
Uncaught TypeError: reactDOMServer.renderToPipeableStream is not a function
at new Promise (<anonymous>)
at next [as next] (<anonymous>)
Uncaught TypeError: reactDOMServer.renderToPipeableStream is not a function
at new Promise (<anonymous>)
at next [as next] (<anonymous>)
. Anyone has any idea on how to fix this?
erquhart
erquhart2mo ago
That should mean you somehow ended up running an older version of @react-email/render. You can run npm ls @react-email/render to be sure, should be at least 1.1.2
Zack07
Zack072mo ago
Thank you
Gil Penner
Gil Penner2mo ago
Just ran into this issue and my workaround was as shown in this picture, I am use 'use node' directive and then I replaced internalMutation to internalAction, have all the latest packages
No description
Diogo
Diogo2mo ago
No description
Diogo
Diogo2mo ago
No description
No description
Diogo
Diogo2mo ago
No description
Diogo
Diogo2mo ago
does anyone has any idea why?
erquhart
erquhart2mo ago
Wherever you're sending the email from, the polyfill isn't getting imported
Diogo
Diogo2mo ago
No description
Diogo
Diogo2mo ago
Is this not enought?
Diogo
Diogo2mo ago
No description
Diogo
Diogo2mo ago
No description
Diogo
Diogo2mo ago
This part of being unable to setup a basic auth with otp is getting frustating @erquhart any ideias? I can upload my code to github is that helps
erquhart
erquhart3w ago
Hard to say, but there's a working implementation here, maybe you can compare and contrast (it's better-auth, but the problem you're hitting should apply to convex auth as well): https://github.com/get-convex/better-auth/blob/main/examples/react/convex/email.tsx Oh we migrated that to the Resend component - that also works, but here's how it looked when using Resend directly: https://github.com/get-convex/better-auth/blob/7991bc7ffb182fb5da0abf72618a49fb5d42a1ae/examples/react/convex/email.tsx

Did you find this page helpful?