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
- Set provider.from to a full email address including @, e.g. "Auth.js <no-reply@yourdomain.com>"
- Validate the MAILGUN_FROM env var at startup (regex for user@domain)
- Confirm the domain part is verified in your Mailgun account (next failure would otherwise be an API error)
- 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
- Always configure from as "Name <user@domain.com>", never a bare display name
- Validate the MAILGUN_FROM env var at startup with an email regex
- Ensure the domain part matches a verified Mailgun domain
- Log the resolved provider config once at boot to catch empty values
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Missing email from request body.
- Invalid email address format.
- Missing Loops API Key or TransactionalId
- Mailgun error: + (await res.text())
- Missing Postmark API Key
AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28).
Data as JSON: /api/errors/152e945cb647ca18.
Report an issue: GitHub.