Nodemailer Causes Runtime Error
https://docs.convex.dev/functions/runtimes#nodejs-runtime
Looks like node mailer causes trouble with convex and opting in doesn't cut it.
Runtimes | Convex Developer Hub
Convex functions can run in two runtimes:

3 Replies
'use node'
import { Email } from "@convex-dev/auth/providers/Email";
import { render } from "@react-email/render";
import nodemailer from "nodemailer";
import { alphabet, generateRandomString } from "oslo/crypto";
import { PasswordResetEmail } from "./PasswordResetEmail";
export const NodemailerOTPPasswordReset = Email({
id: "nodemailer-otp-password-reset",
apiKey: "not-used-with-nodemailer", // This is required by the Email provider but not used
async generateVerificationToken() {
return generateRandomString(8, alphabet("0-9"));
},
async sendVerificationRequest({
identifier: email,
token,
expires,
}) {
// Create a nodemailer transporter using Amazon SES SMTP
const transporter = nodemailer.createTransport({
host: process.env.AUTH_SES_SMTP_HOST,
port: Number.parseInt(process.env.AUTH_SES_SMTP_PORT || "587"),
secure: process.env.AUTH_SES_SMTP_SECURE === "true",
auth: {
user: process.env.AUTH_SES_SMTP_USER,
pass: process.env.AUTH_SES_SMTP_PASSWORD,
},
});
// Render the React email template to HTML
const html = await render(PasswordResetEmail({ code: token, expires }));
// Send the email
const result = await new Promise<{ error?: Error; info?: nodemailer.SentMessageInfo }>((resolve) => {
transporter.sendMail(
{
from: process.env.AUTH_EMAIL ?? "My App <noreply@example.com>",
to: email,
subject: "Reset password in My App",
html,
},
(err, info) => {
if (err) {
resolve({ error: err });
} else {
resolve({ info });
}
}
);
});
if (result.error) {
//
}
},
})
'use node'
import { Email } from "@convex-dev/auth/providers/Email";
import { render } from "@react-email/render";
import nodemailer from "nodemailer";
import { alphabet, generateRandomString } from "oslo/crypto";
import { PasswordResetEmail } from "./PasswordResetEmail";
export const NodemailerOTPPasswordReset = Email({
id: "nodemailer-otp-password-reset",
apiKey: "not-used-with-nodemailer", // This is required by the Email provider but not used
async generateVerificationToken() {
return generateRandomString(8, alphabet("0-9"));
},
async sendVerificationRequest({
identifier: email,
token,
expires,
}) {
// Create a nodemailer transporter using Amazon SES SMTP
const transporter = nodemailer.createTransport({
host: process.env.AUTH_SES_SMTP_HOST,
port: Number.parseInt(process.env.AUTH_SES_SMTP_PORT || "587"),
secure: process.env.AUTH_SES_SMTP_SECURE === "true",
auth: {
user: process.env.AUTH_SES_SMTP_USER,
pass: process.env.AUTH_SES_SMTP_PASSWORD,
},
});
// Render the React email template to HTML
const html = await render(PasswordResetEmail({ code: token, expires }));
// Send the email
const result = await new Promise<{ error?: Error; info?: nodemailer.SentMessageInfo }>((resolve) => {
transporter.sendMail(
{
from: process.env.AUTH_EMAIL ?? "My App <noreply@example.com>",
to: email,
subject: "Reset password in My App",
html,
},
(err, info) => {
if (err) {
resolve({ error: err });
} else {
resolve({ info });
}
}
);
});
if (result.error) {
//
}
},
})
the "use node" should be at the top of the files that define actions using this library. so if
NodemailerOTPPasswordReset
is used in any other files, you also should put "use node" at the top of those files'use node'
import Apple from "@auth/core/providers/apple";
import GitHub from "@auth/core/providers/github";
import Google from "@auth/core/providers/google";
import Resend from "@auth/core/providers/resend";
import { Anonymous } from "@convex-dev/auth/providers/Anonymous";
import { Password } from "@convex-dev/auth/providers/Password";
import { convexAuth } from "@convex-dev/auth/server";
import { ConvexError } from "convex/values";
import type { DataModel } from "./_generated/dataModel.js";
import { INVALID_PASSWORD } from "./errors.js"
import { NodemailerOTP } from "./otp/NodemailerOTP";
import { TwilioOTP } from "./otp/TwilioOTP";
import { TwilioVerify } from "./otp/TwilioVerify";
import { NodemailerOTPPasswordReset } from "./passwordReset/NodemailerOTPPasswordReset";
//
'use node'
import Apple from "@auth/core/providers/apple";
import GitHub from "@auth/core/providers/github";
import Google from "@auth/core/providers/google";
import Resend from "@auth/core/providers/resend";
import { Anonymous } from "@convex-dev/auth/providers/Anonymous";
import { Password } from "@convex-dev/auth/providers/Password";
import { convexAuth } from "@convex-dev/auth/server";
import { ConvexError } from "convex/values";
import type { DataModel } from "./_generated/dataModel.js";
import { INVALID_PASSWORD } from "./errors.js"
import { NodemailerOTP } from "./otp/NodemailerOTP";
import { TwilioOTP } from "./otp/TwilioOTP";
import { TwilioVerify } from "./otp/TwilioVerify";
import { NodemailerOTPPasswordReset } from "./passwordReset/NodemailerOTPPasswordReset";
//