nextauthjs/next-auth · error · Error

malformed Mailgun domain

Error message

malformed Mailgun domain

What it means

The Mailgun provider derives the sending domain by splitting provider.from on "@" and taking the last part; if there is no "@" (or the part is falsy) it cannot build the Mailgun API URL and throws this Error. It guards against a malformed from address that would make the domain-based API endpoint invalid.

Source

Thrown at packages/core/src/providers/mailgun.ts:77

  const { region = "US" } = config
  const servers = {
    US: "api.mailgun.net",
    EU: "api.eu.mailgun.net",
  }
  const apiServer = servers[region]

  return {
    id: "mailgun",
    type: "email",
    name: "Mailgun",
    from: "Auth.js <no-reply@authjs.dev>",
    maxAge: 24 * 60 * 60,
    async sendVerificationRequest(params) {
      const { identifier: to, provider, url, theme } = params
      const { host } = new URL(url)
      const domain = provider.from?.split("@").at(1)

      if (!domain) throw new Error("malformed Mailgun domain")

      const form = new FormData()
      form.append("from", `${provider.name} <${provider.from}>`)
      form.append("to", to)
      form.append("subject", `Sign in to ${host}`)
      form.append("html", html({ host, url, theme }))
      form.append("text", text({ host, url }))

      const res = await fetch(`https://${apiServer}/v3/${domain}/messages`, {
        method: "POST",
        headers: {
          Authorization: `Basic ${btoa(`api:${provider.apiKey}`)}`,
        },
        body: form,
      })

      if (!res.ok) throw new Error("Mailgun error: " + (await res.text()))
    },

View on GitHub (pinned to a1a16a5a77)

Solutions

  1. Set provider.from to a full email address including @, e.g. "Auth.js <no-reply@yourdomain.com>"
  2. Validate the MAILGUN_FROM env var at startup (regex for user@domain)
  3. Confirm the domain part is verified in your Mailgun account (next failure would otherwise be an API error)
  4. Log the resolved provider config once at boot to catch empty values early

Example fix

// before
from: process.env.MAILGUN_FROM || "no-reply" // no @ -> throw
// after
from: process.env.MAILGUN_FROM ?? "Auth.js <no-reply@mg.yourdomain.com>"
Defensive patterns

Strategy: validation

Validate before calling

const from = process.env.MAILGUN_FROM
if (!from || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(from.replace(/^[^<]*<|>.*$/g, ''))) {
  throw new Error('MAILGUN_FROM must be a full email address like "Auth.js <no-reply@example.com>"')
}

Type guard

function hasMailgunDomain(p: { from?: string }): p is { from: string } & { domain: string } {
  const domain = p.from?.split('@').at(1)
  return typeof domain === 'string' && domain.length > 0
}

Try / catch

try {
  await sendVerificationRequest(params)
} catch (e) {
  if (e instanceof Error && e.message === 'malformed Mailgun domain') {
    console.error('Mailgun provider.from must include an @domain part')
  }
}

Prevention

When it happens

Trigger: sendVerificationRequest with provider.from set to a string without an "@" — e.g. from: "Auth.js <no-reply>" or from: undefined — so `provider.from?.split("@").at(1)` yields undefined and `if (!domain)` throws.

Common situations: Copy-pasting a display name only ("Auth.js <no-reply@authjs.dev>" truncated), leaving from unset and relying on a default that has no address, or building the config from env vars where MAILGUN_FROM is empty or malformed.

Understand the failure class

Related errors


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