nextauthjs/next-auth · error · Error

Email (${failed.join(", ")}) could not be sent

Error message

Email (${failed.join(", ")}) could not be sent

What it means

After transport.sendMail resolves, the provider inspects result.rejected and result.pending; if any recipient addresses were rejected or left pending, it throws this Error listing them. Nodemailer accepted the message for some recipients but the SMTP server refused others, so the verification email did not reach the user and sign-in fails.

Source

Thrown at packages/core/src/providers/nodemailer.ts:79

    server: { host: "localhost", port: 25, auth: { user: "", pass: "" } },
    from: "Auth.js <no-reply@authjs.dev>",
    maxAge: 24 * 60 * 60,
    async sendVerificationRequest(params) {
      const { identifier, url, provider, theme } = params
      const { host } = new URL(url)
      const transport = createTransport(provider.server)
      const result = await transport.sendMail({
        to: identifier,
        from: provider.from,
        subject: `Sign in to ${host}`,
        text: text({ url, host }),
        html: html({ url, host, theme }),
      })
      const rejected = result.rejected || []
      const pending = result.pending || []
      const failed = rejected.concat(pending).filter(Boolean)
      if (failed.length) {
        throw new Error(`Email (${failed.join(", ")}) could not be sent`)
      }
    },
    options: config,
  }
}

View on GitHub (pinned to a1a16a5a77)

Solutions

  1. Check the listed addresses in the error — verify the user typed a valid, deliverable email
  2. Review your SMTP server logs for the corresponding rejection reason (550/551/greylist)
  3. If greylisting causes pending entries, configure your SMTP server or retry the send after a delay
  4. Test with a known-good recipient to confirm the transport itself is healthy

Example fix

// before
// spam filter rejects recipients silently at SMTP level -> throw
server: { host: "smtp.example.com", port: 25 }
// after
server: { host: "smtp.example.com", port: 587, auth: {...} }
// plus: validate the identifier is a syntactically valid email before calling signIn,
// and catch this error in the sign-in flow to show a user-friendly message
Defensive patterns

Strategy: try-catch

Validate before calling

// validate the submitted identifier before triggering signIn
const emailOk = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(identifier)
if (!emailOk) return rejectSignIn('Please enter a valid email address')

Type guard

function wasFullyAccepted(r: { rejected?: string[]; pending?: string[]; accepted?: string[] }): boolean {
  return (r.rejected?.length ?? 0) === 0 && (r.pending?.length ?? 0) === 0
}

Try / catch

try {
  await sendVerificationRequest(params)
} catch (e) {
  const m = e instanceof Error ? e.message : ''
  const failed = m.match(/Email \((.+)\) could not be sent/)?.[1]
  console.error(`SMTP rejected recipients: ${failed} — check SMTP server logs`)
  // return a neutral message to the user; do not reveal whether the address exists
}

Prevention

When it happens

Trigger: transport.sendMail({ to, subject, text, html }) returns a result where rejected.concat(pending).filter(Boolean).length > 0 — e.g. the SMTP server rejects the recipient address (550 no such user, greylisting placing it in pending, spam policy refusal). Any non-empty failed list triggers the throw with the addresses joined by commas.

Common situations: Typo'd or disposable email addresses submitted on the sign-in form; corporate SMTP rejecting external recipients; self-hosted SMTP with strict recipient validation; greylisting providers returning the recipient as pending.

Related errors


AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28). Data as JSON: /api/errors/7434529498c1ae8e. Report an issue: GitHub.