Nodemailer

nodemailer Missing Credentials for Plain

Nodemailer throws this before login when auth.user or auth.pass is undefined. Pass keys user and pass (not password) in the auth object and check env vars.

Updated Jul 1, 2026

The short answer

"Missing credentials for PLAIN" is a Nodemailer (not SMTP server) error thrown before login: your transport was told to authenticate, but auth.user or auth.pass resolved to undefined. Fix it by passing a valid auth object with the exact keys user and pass (not password) and confirming any environment variables are actually loaded and non-empty.

What causes the Nodemailer "Missing credentials for PLAIN" error?

This is a client-side Nodemailer error, not a reply from your SMTP server. Nodemailer raises it inside SMTPConnection before it ever sends an AUTH PLAIN command — at the moment it prepares to authenticate, it finds that the username or password it was given is empty or undefined. PLAIN is simply the SASL mechanism (defined in RFC 4616; the SMTP AUTH extension that carries it is RFC 4954) Nodemailer defaults to, so the error names it even though the real problem is missing input, not a rejected login.

The common triggers, in order of frequency:

  1. auth.pass or auth.user is undefined. Almost always because an environment variable was read before it was loaded, was never set, or has a typo. (See Nodemailer issue #1651, where the auth object read process.env.passwordForNodeMailer — a variable name that never appears as set in the reporter's .env excerpt, consistent with it resolving to undefined.)
  2. Wrong property name. Nodemailer reads pass, not password. Writing password: silently leaves pass undefined.
  3. Malformed config. The credentials aren't inside an auth object, or the object key is misspelled (e.g. providerauth). The previous version of this page contained exactly this bug.
  4. Deploying without env vars set (Heroku Config Vars, Vercel/Render env settings, Docker secrets), so the values are present locally but undefined in production.

How do I fix "Missing credentials for PLAIN"?

1. Use a correctly shaped transport. The auth object must contain user and pass:

const nodemailer = require("nodemailer");
const transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 465, // 465 (implicit TLS) or 587 (STARTTLS) — both valid (RFC 8314)
secure: true, // true for 465, false for 587
auth: {
user: process.env.SMTP_USER, // your full email address
pass: process.env.SMTP_PASS, // the SMTP password / app password — key is "pass", NOT "password"
},
});

2. Prove the values are actually defined. Log them (length only, never the secret) right before creating the transport:

console.log("user set?", Boolean(process.env.SMTP_USER), "pass length:", (process.env.SMTP_PASS || "").length);

If either is false/0, the credential never reached Nodemailer — that is your bug, not the SMTP server.

3. Load .env before anything reads it. With dotenv, call config at the very top, before you import the module that builds the transport:

require("dotenv").config(); // must run before process.env is read

4. In production, set the variables on the platform. On Heroku: Settings → Reveal Config Vars and add each key. On Vercel/Render/Railway, use the project's Environment settings. Redeploy so the new values take effect.

5. Gmail / Google Workspace: Do not use your normal account password, and do not look for "Less Secure Apps" — Google removed that toggle in 2022. Enable 2-Step Verification, then generate a 16-character App Password (Google support) and use it as auth.pass. For org-wide control, OAuth2 is the alternative.

6. Microsoft 365 / Outlook: SMTP AUTH is disabled by default on many tenants. An admin must enable Authenticated SMTP for the mailbox (Microsoft 365 admin center → user → Mail → Manage email apps), otherwise valid credentials still fail.

If you genuinely don't want to authenticate (e.g. an internal relay or MailHog/Mailtrap that accepts mail without login), omit the auth object entirely rather than passing one with empty fields — an auth object with blank values is what triggers this error.

FAQ

Common questions

It is a Nodemailer client-side error thrown before any AUTH command is sent. PLAIN refers to the SASL auth mechanism Nodemailer was about to use, but the error fires because auth.user or auth.pass was empty or undefined, not because the server rejected the login.

One API, every provider

Stop debugging raw provider errors

Courier connects to your email, SMS, and push providers, handles retries and failover, and surfaces delivery errors in plain language.

Reply-code definitions per RFC 4616. Last reviewed Jul 1, 2026. Courier is not affiliated with third-party providers; error behavior may vary by implementation.