react-email icon indicating copy to clipboard operation
react-email copied to clipboard

`TypeError: dispatcher.getOwner is not a function` thrown when rendering an email template with React 19 RC

Open miloschwartz opened this issue 1 year ago • 7 comments

Describe the Bug

The following error is thrown when rendering an email template in a containerized Next.js 15 application using React 19.0.0-rc.

 TypeError: dispatcher.getOwner is not a function
pangolin  |     at getOwner (/app/node_modules/react/cjs/react-jsx-runtime.development.js:349:54)
pangolin  |     at jsxDEVImpl (/app/node_modules/react/cjs/react-jsx-runtime.development.js:523:57)
pangolin  |     at process.env.NODE_ENV.exports.jsx (/app/node_modules/react/cjs/react-jsx-runtime.development.js:651:14)
pangolin  |     at Html (file:///app/node_modules/@react-email/html/dist/index.mjs:39:28)
pangolin  |     at renderWithHooks (/app/node_modules/react-dom/cjs/react-dom-server.node.production.js:4086:18)
pangolin  |     at renderElement (/app/node_modules/react-dom/cjs/react-dom-server.node.production.js:4482:18)
pangolin  |     at retryNode (/app/node_modules/react-dom/cjs/react-dom-server.node.production.js:4768:16)
pangolin  |     at renderNodeDestructive (/app/node_modules/react-dom/cjs/react-dom-server.node.production.js:4586:7)
pangolin  |     at renderNode (/app/node_modules/react-dom/cjs/react-dom-server.node.production.js:5025:14)
pangolin  |     at renderElement (/app/node_modules/react-dom/cjs/react-dom-server.node.production.js:4420:18)

Interestingly rendering works completely free of errors when running a dev server. I am using the following packages and versions.

{
    "dependencies": {
        "@hookform/resolvers": "3.9.0",
        "@node-rs/argon2": "1.8.3",
        "@oslojs/crypto": "1.0.1",
        "@oslojs/encoding": "1.1.0",
        "@radix-ui/react-avatar": "1.1.1",
        "@radix-ui/react-checkbox": "1.1.2",
        "@radix-ui/react-dialog": "1.1.2",
        "@radix-ui/react-dropdown-menu": "2.1.2",
        "@radix-ui/react-icons": "1.3.0",
        "@radix-ui/react-label": "2.1.0",
        "@radix-ui/react-popover": "1.1.2",
        "@radix-ui/react-radio-group": "1.2.1",
        "@radix-ui/react-select": "2.1.2",
        "@radix-ui/react-separator": "1.1.0",
        "@radix-ui/react-slot": "1.1.0",
        "@radix-ui/react-switch": "1.1.1",
        "@radix-ui/react-tabs": "1.1.1",
        "@radix-ui/react-toast": "1.2.2",
        "@react-email/components": "0.0.28",
        "@react-email/tailwind": "1.0.2",
        "@tanstack/react-table": "8.20.5",
        "axios": "1.7.7",
        "better-sqlite3": "11.3.0",
        "class-variance-authority": "0.7.0",
        "clsx": "2.1.1",
        "cmdk": "1.0.0",
        "cookie-parser": "1.4.6",
        "cors": "2.8.5",
        "drizzle-orm": "0.33.0",
        "emblor": "1.4.6",
        "eslint": "9.15.0",
        "eslint-config-next": "15.0.3",
        "express": "4.21.0",
        "express-rate-limit": "7.4.0",
        "glob": "11.0.0",
        "helmet": "7.1.0",
        "http-errors": "2.0.0",
        "input-otp": "1.2.4",
        "js-yaml": "4.1.0",
        "lucide-react": "0.447.0",
        "moment": "2.30.1",
        "next": "15.0.1",
        "next-themes": "0.3.0",
        "node-fetch": "3.3.2",
        "nodemailer": "6.9.15",
        "oslo": "1.2.1",
        "react": "19.0.0-rc.1",
        "react-dom": "19.0.0-rc.1",
        "react-hook-form": "7.53.0",
        "rebuild": "0.1.2",
        "tailwind-merge": "2.5.3",
        "tailwindcss-animate": "1.0.7",
        "vaul": "1.1.1",
        "winston": "3.14.2",
        "winston-daily-rotate-file": "5.0.0",
        "ws": "8.18.0",
        "zod": "3.23.8",
        "zod-validation-error": "3.4.0"
    },
    "devDependencies": {
        "react-email": "3.0.2",
        "@dotenvx/dotenvx": "1.14.2",
        "@esbuild-plugins/tsconfig-paths": "0.1.2",
        "@types/better-sqlite3": "7.6.11",
        "@types/cookie-parser": "1.4.7",
        "@types/cors": "2.8.17",
        "@types/express": "5.0.0",
        "@types/js-yaml": "4.0.9",
        "@types/node": "^20",
        "@types/nodemailer": "6.4.16",
        "@types/react": "npm:[email protected]",
        "@types/react-dom": "npm:[email protected]",
        "@types/ws": "8.5.13",
        "@types/yargs": "17.0.33",
        "drizzle-kit": "0.24.2",
        "esbuild": "0.20.1",
        "esbuild-node-externals": "1.13.0",
        "postcss": "^8",
        "tailwindcss": "^3.4.1",
        "tsc-alias": "1.8.10",
        "tsx": "4.19.1",
        "typescript": "^5",
        "yargs": "17.7.2"
    },
    "overrides": {
        "@types/react": "npm:[email protected]",
        "@types/react-dom": "npm:[email protected]"
    }
}

Which package is affected (leave empty if unsure)

No response

Link to the code that reproduces this issue

none

To Reproduce

Calling render from causes the error. The second log statement is never executed in the below example.

import { render } from "@react-email/render";
import { ReactElement } from "react";
    
logger.debug("Rendering email templatee...")
const emailHtml = await render(template);
logger.debug("Done rendering email templatee")

I also tried:

import { render } from "@react-email/components";

Expected Behavior

I expect the render function to produce the rendered HTML code without throwing an error.

What's your node version? (if relevant)

20.10.0

miloschwartz avatar Nov 26 '24 22:11 miloschwartz

By tracing the error I noticed this has to do with NODE_ENV being set to production. This makes since as it worked for me in dev mode, but not in a production build. The following files are key:

node_modules/react/jsx-runtime.js

'use strict';

if (process.env.NODE_ENV === 'production') {
  module.exports = require('./cjs/react-jsx-runtime.production.js');
} else {
  module.exports = require('./cjs/react-jsx-runtime.development.js');
}

node_modules/react/cjs/react-jsx-runtime.production.js

"use strict";
var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"),
  REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
function jsxProd(type, config, maybeKey) {
  var key = null;
  void 0 !== maybeKey && (key = "" + maybeKey);
  void 0 !== config.key && (key = "" + config.key);
  if ("key" in config) {
    maybeKey = {};
    for (var propName in config)
      "key" !== propName && (maybeKey[propName] = config[propName]);
  } else maybeKey = config;
  config = maybeKey.ref;
  return {
    $$typeof: REACT_ELEMENT_TYPE,
    type: type,
    key: key,
    ref: void 0 !== config ? config : null,
    props: maybeKey
  };
}
exports.Fragment = REACT_FRAGMENT_TYPE;
exports.jsx = jsxProd;
exports.jsxs = jsxProd;

node_modules/react/cjs/react-jsx-runtime.development.js

...
              return describeUnknownElementTypeFrameInDEV(type(prototype));
            } catch (x) {}
        }
      return "";
    }
    function getOwner() {
      var dispatcher = ReactSharedInternals.A;
      return null === dispatcher ? null : dispatcher.getOwner();
    }
    function hasValidKey(config) {
      if (hasOwnProperty.call(config, "key")) {
        var getter = Object.getOwnPropertyDescriptor(config, "key").get;
        if (getter && getter.isReactWarning) return !1;
      }
      return void 0 !== config.key;
    }
...

Notice how the production version does not have getOwner while the development version does. Setting NODE_ENV to development fixed the issue for me and I am able to use the render function as expected.

This leaves me to question whether I configuring something incorrectly or if this is a real bug. Please let me know.

miloschwartz avatar Nov 27 '24 19:11 miloschwartz

This seems like an oddly specific issue, we'll need a minimal reproduction for it. Can you make one?

gabrielmfern avatar Dec 02 '24 15:12 gabrielmfern

I am having the same problem, does anyone have a solution?

tientran0019 avatar Dec 08 '24 13:12 tientran0019

I also have this issue. Can't really find anything online...

thevalleyy avatar Jan 20 '25 08:01 thevalleyy

The same issue

igorogerchuk avatar Jan 21 '25 14:01 igorogerchuk

https://github.com/remix-run/react-router/issues/12138 provides a fix

thevalleyy avatar Jan 21 '25 14:01 thevalleyy

I'm not using react-email but I had same issues with react-router. In my case in such as issue I had to use NODE_ENV=production or use production build because I used two react apps with different builds.

zdunecki avatar Feb 09 '25 17:02 zdunecki

Closing as not planned, assuming the issue is not present anymore. If anyone has this problem again, make a minimal reproduction and I'll reopen

gabrielmfern avatar May 28 '25 15:05 gabrielmfern

It is happening for me.

Here is a v0 repro: https://v0.dev/chat/fork-of-wysiwyg-email-editor-zJ6bz6PKqjY

Click on Export HTML button to see the error.

rajat1saxena avatar Jun 14 '25 04:06 rajat1saxena

@rajat1saxena can't use that, too big to work with. Make a minimal one.

But just looking at it from the surface, it might be that some invalid type was passed to the JSX factory

gabrielmfern avatar Jun 16 '25 12:06 gabrielmfern

@gabrielmfern here's a smaller v0 example: https://v0.dev/chat/react-email-error-getowner-TK7taTkIs98

and the v0 logs:

17:48:15.888Z [SERVER] Error rendering email: h.getOwner is not a function. (In 'h.getOwner()', 'h.getOwner' is undefined) 17:48:15.889Z [SERVER] Unhandled promise rejection: TypeError: ReadableByteStreamController is not implemented

namaiki-llc avatar Jun 28 '25 00:06 namaiki-llc

By tracing the error I noticed this has to do with NODE_ENV being set to production. This makes since as it worked for me in dev mode, but not in a production build. The following files are key:

node_modules/react/jsx-runtime.js

'use strict';

if (process.env.NODE_ENV === 'production') { module.exports = require('./cjs/react-jsx-runtime.production.js'); } else { module.exports = require('./cjs/react-jsx-runtime.development.js'); } node_modules/react/cjs/react-jsx-runtime.production.js

"use strict"; var REACT_ELEMENT_TYPE = Symbol.for("react.transitional.element"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"); function jsxProd(type, config, maybeKey) { var key = null; void 0 !== maybeKey && (key = "" + maybeKey); void 0 !== config.key && (key = "" + config.key); if ("key" in config) { maybeKey = {}; for (var propName in config) "key" !== propName && (maybeKey[propName] = config[propName]); } else maybeKey = config; config = maybeKey.ref; return { $$typeof: REACT_ELEMENT_TYPE, type: type, key: key, ref: void 0 !== config ? config : null, props: maybeKey }; } exports.Fragment = REACT_FRAGMENT_TYPE; exports.jsx = jsxProd; exports.jsxs = jsxProd; node_modules/react/cjs/react-jsx-runtime.development.js

... return describeUnknownElementTypeFrameInDEV(type(prototype)); } catch (x) {} } return ""; } function getOwner() { var dispatcher = ReactSharedInternals.A; return null === dispatcher ? null : dispatcher.getOwner(); } function hasValidKey(config) { if (hasOwnProperty.call(config, "key")) { var getter = Object.getOwnPropertyDescriptor(config, "key").get; if (getter && getter.isReactWarning) return !1; } return void 0 !== config.key; } ... Notice how the production version does not have getOwner while the development version does. Setting NODE_ENV to development fixed the issue for me and I am able to use the render function as expected.

This leaves me to question whether I configuring something incorrectly or if this is a real bug. Please let me know.

This was the exact reason in my case. react use NODE_ENV as the env variable name in react package, I was also using NODE_ENV as the env name to keep my application's environment. Both conflicted in the production. Could resolve it by changing my application's NODE_ENV to another name(ex: APP_ENV) my application.

madawa-prasad96 avatar Oct 01 '25 10:10 madawa-prasad96