Next.js Discord

Discord Forum

Build fails with Server Action in Root Layout

Answered
American Crow posted this in #help-forum
Open in Discord
American CrowOP
I have a Button <ContactCallToAction /> which opens a form to send emails using nodemailer inside a server action.
I use this Component on multiple places in my code and it works everywhere expect when i put in the the nav, which itself is loaded from the RootLayout.

When i do so i get this error when building:
./node_modules/nodemailer/lib/dkim/index.js:11:1
Module not found: Can't resolve 'path'
   9 | const PassThrough = require('stream').PassThrough;
  10 | const fs = require('fs');
> 11 | const path = require('path');
     | ^
  12 | const crypto = require('crypto');
  13 |
  14 | const DKIM_ALGO = 'sha256';

https://nextjs.org/docs/messages/module-not-found

Import trace for requested module:
./node_modules/nodemailer/lib/mailer/index.js
./node_modules/nodemailer/lib/nodemailer.js
./src/components/contact/action-contact-form-send.tsx
./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Fzomh%2Fprojects%2Fcapsloq-v5%2Fsrc%2Fcomponents%2Fcontact%2Faction-contact-form-send.tsx%22%2C%5B%22default%22%5D%5D%5D&__client_imported__=true!

./node_modules/nodemailer/lib/dkim/index.js:12:1
Module not found: Can't resolve 'crypto'
  10 | const fs = require('fs');
  11 | const path = require('path');
> 12 | const crypto = require('crypto');
     | ^
  13 |
  14 | const DKIM_ALGO = 'sha256';
  15 | const MAX_MESSAGE_SIZE = 128 * 1024; // buffer messages larger than this to disk

https://nextjs.org/docs/messages/module-not-found

Which to me indicates that it's run on edge (cause crypto/fs not available) or something is not available at build time. ( I can run it in dev mode and the button works in the nav). I thought I read something about that but I can't remember exactly.

Let me know if you need to see implenentation of <ContactCallToAction /> or the corresponding server action
Answered by joulev
The cause is this:

/some-child is edge. Which means, all server actions executed on /some-child run on the edge runtime. Since the button is rendered in the layout, it is also rendered in the /some-child route, which means it's possible to trigger the nodemailer action in the /some-child route.

But nodemailer doesn't support the edge runtime. So when bundling the server-side part of your /some-child route, Next.js finds this issue and throws the build error.

There are a few ways to fix this, for example (sorted in my order of preference):

* Find an edge-compatible library/method to send emails. Example: https://www.reddit.com/r/nextjs/comments/16raleu/email_functionality_for_edge_runtime/
* Just don't use edge – Vercel AI SDK works on node too
* Rearrange your page somehow so that it is impossible to trigger the nodemailer action from edge pages
* Use a nodejs route handler to send the email, and in the server action you fetch that route handler
View full answer

17 Replies

American CrowOP
I've tried dynamically (lazy) loading <ContactCallToAction /> with ssr:false
I've tried export const runtime = 'nodejs' in root layout
Same error
American CrowOP
@joulev sorry for the ping but i think we had that recently in #discussions and you answered it. I just cant remember the details >_<
@American Crow I have a Button `<ContactCallToAction />` which opens a form to send emails using nodemailer inside a server action. I use this Component on multiple places in my code and it works everywhere expect when i put in the the nav, which itself is loaded from the RootLayout. When i do so i get this error when building: tsx ./node_modules/nodemailer/lib/dkim/index.js:11:1 Module not found: Can't resolve 'path' 9 | const PassThrough = require('stream').PassThrough; 10 | const fs = require('fs'); > 11 | const path = require('path'); | ^ 12 | const crypto = require('crypto'); 13 | 14 | const DKIM_ALGO = 'sha256'; https://nextjs.org/docs/messages/module-not-found Import trace for requested module: ./node_modules/nodemailer/lib/mailer/index.js ./node_modules/nodemailer/lib/nodemailer.js ./src/components/contact/action-contact-form-send.tsx ./node_modules/next/dist/build/webpack/loaders/next-flight-action-entry-loader.js?actions=%5B%5B%22%2Fhome%2Fzomh%2Fprojects%2Fcapsloq-v5%2Fsrc%2Fcomponents%2Fcontact%2Faction-contact-form-send.tsx%22%2C%5B%22default%22%5D%5D%5D&__client_imported__=true! ./node_modules/nodemailer/lib/dkim/index.js:12:1 Module not found: Can't resolve 'crypto' 10 | const fs = require('fs'); 11 | const path = require('path'); > 12 | const crypto = require('crypto'); | ^ 13 | 14 | const DKIM_ALGO = 'sha256'; 15 | const MAX_MESSAGE_SIZE = 128 * 1024; // buffer messages larger than this to disk https://nextjs.org/docs/messages/module-not-found Which to me indicates that it's run on edge (cause crypto/fs not available) or something is not available at build time. ( I can run it in dev mode and the button works in the nav). I thought I read something about that but I can't remember exactly. Let me know if you need to see implenentation of `<ContactCallToAction />` or the corresponding server action
The error looks weird. Can you reduce the repo into a minimal state and send me the link?
This looks like it’s related to some weird bundling bugs related to nodemailer
American CrowOP
Oh okay that would be something entirely different
Than what i thought
Yea let me make a minimal repo
@joulev The error looks weird. Can you reduce the repo into a minimal state and send me the link?
American CrowOP
Don't think i went afk, I am trying to reproduce now for 4.5 hours?
Ofc creating a minimal version the error does not appear so i am testing everything in the main project to make it as similar as possible.
I went full paranoia and adjusted every minor version of every package which both environments use.
I can't find the error this fucking code is cursed. Everything is 1:1 i double checked everything from tsconfig to nextjs config hell even eslint.
Ofc the main project got some additional dependencies but that's it.

Both ways i cant make the error appear in the minimal repo nor can i fix it in the main project lmao. I am gonna take a break
@joulev A good way to make a minimal repo: https://nextjs-faq.com/minimal-reproduction-repository
American CrowOP
Thank you for that i finally found where the bug comes from by follwing your advice.
I have an unrelated route /ai-stream which has it's own layout and page.tsx. They both use export const runtime = 'edge'. If i comment the runtime exports both out the build error disappears. I don't know how, since the /ai-stream route does not import the server action which is using nodemailer it's independent (It's also not a child of the layout).

This is a weird one
https://github.com/capsloq/nodemailer-minimal

In the repo you can either:
- Remove the button from the layout -> build succeeds
or
- comment out export const runtime = 'edge' from /some-child -> build succeeds.

I don't know whats happening. I'd love to use both since i need the edge runtime for my ai streaming.
But i am at the end of my knowledge
100% cursed
@joulev Ok this is insane. The bug is utterly absurd and at first glance I can’t see what’s wrong either. Tomorrow I’ll try to run the project and debug
American CrowOP
Yea okay thank you. At least you can understand why it took me so freaking long to find / reproduce this one 😭 . Thanks in advanace for having a look!
@American Crow Thank you for that i finally found where the bug comes from by follwing your advice. I have an unrelated route `/ai-stream` which has it's own `layout` and `page.tsx`. They both use `export const runtime = 'edge'`. If i comment the `runtime` exports both out the build error disappears. I don't know how, since the `/ai-stream` route does not import the server action which is using `nodemailer` it's independent (It's also not a child of the layout). This is a weird one https://github.com/capsloq/nodemailer-minimal In the repo you can either: - Remove the button from the layout -> build succeeds or - comment out `export const runtime = 'edge'` from `/some-child` -> build succeeds. I don't know whats happening. I'd love to use both since i need the edge runtime for my ai streaming. But i am at the end of my knowledge 100% cursed
The cause is this:

/some-child is edge. Which means, all server actions executed on /some-child run on the edge runtime. Since the button is rendered in the layout, it is also rendered in the /some-child route, which means it's possible to trigger the nodemailer action in the /some-child route.

But nodemailer doesn't support the edge runtime. So when bundling the server-side part of your /some-child route, Next.js finds this issue and throws the build error.

There are a few ways to fix this, for example (sorted in my order of preference):

* Find an edge-compatible library/method to send emails. Example: https://www.reddit.com/r/nextjs/comments/16raleu/email_functionality_for_edge_runtime/
* Just don't use edge – Vercel AI SDK works on node too
* Rearrange your page somehow so that it is impossible to trigger the nodemailer action from edge pages
* Use a nodejs route handler to send the email, and in the server action you fetch that route handler
Answer