danny-avila/LibreChat · error · Error
Failed to send email via Mailgun
Error message
Failed to send email via Mailgun
What it means
Thrown by sendEmailViaMailgun when the axios POST to `${mailgunHost}/v3/${mailgunDomain}/messages` fails. The underlying axios error is formatted by logAxiosError and re-thrown as a single Error whose message includes the HTTP status and Mailgun response body. Causes range from auth failure to rejected recipients to network errors.
Source
Thrown at api/server/utils/sendEmail.js:47
const formData = new FormData();
formData.append('from', from);
formData.append('to', to);
formData.append('subject', subject);
formData.append('html', html);
formData.append('o:tracking-clicks', 'no');
try {
const response = await axios.post(`${mailgunHost}/v3/${mailgunDomain}/messages`, formData, {
headers: {
...formData.getHeaders(),
Authorization: `Basic ${Buffer.from(`api:${mailgunApiKey}`).toString('base64')}`,
},
});
return response.data;
} catch (error) {
throw new Error(logAxiosError({ error, message: 'Failed to send email via Mailgun' }));
}
};
/**
* Sends an email using SMTP via Nodemailer.
*
* @async
* @function sendEmailViaSMTP
* @param {Object} params - The parameters for sending the email.
* @param {Object} params.transporterOptions - The transporter configuration options.
* @param {Object} params.mailOptions - The email options.
* @returns {Promise<Object>} - A promise that resolves to the info object of the sent email.
*/
const sendEmailViaSMTP = async ({ transporterOptions, mailOptions }) => {
const transporter = nodemailer.createTransport(transporterOptions);
return await transporter.sendMail(mailOptions);
};
View on GitHub (pinned to 5ff282f900)
Solutions
- Read the full logAxiosError message to obtain the exact HTTP status and Mailgun error code.
- For 401, regenerate the API key in Mailgun and update MAILGUN_API_KEY.
- For 400/404, confirm MAILGUN_DOMAIN is a verified domain on that account and that recipients are well-formed.
- For EU accounts, set MAILGUN_HOST to https://api.eu.mailgun.net.
- Clear suppressions (bounces/complaints) for the recipient in the Mailgun dashboard if delivery is blocked.
Example fix
// before
await sendEmailViaMailgun({ to, from, subject, html });
// after (structured failure for the caller)
try {
await sendEmailViaMailgun({ to, from, subject, html });
} catch (err) {
logger.error('Mailgun send failed:', err.message);
throw new Error(`Email delivery failed; please retry. (${err.message})`);
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!process.env.MAILGUN_API_KEY || !process.env.MAILGUN_DOMAIN) {
throw new Error('Mailgun API key and domain are required');
}
if (!to || !from || !subject || !html) {
throw new Error('to, from, subject, and html are required to send email');
} Try / catch
try {
await sendEmailViaMailgun({ to, from, subject, html });
} catch (err) {
if (err.message.startsWith('Failed to send email via Mailgun')) {
logger.error('Mailgun delivery failed:', err.message);
throw new Error('Email delivery failed; please retry.');
}
throw err;
} Prevention
- Rotate keys in Mailgun and update env together.
- Verify MAILGUN_DOMAIN is an approved domain on the account.
- Set MAILGUN_HOST for non-US (EU) accounts.
- Clear suppressions for bounced/complained recipients before retrying.
When it happens
Trigger: HTTP 401 from a wrong/revoked MAILGUN_API_KEY; 400 from a malformed recipient or bad domain; 404 when MAILGUN_DOMAIN does not match the key's account; MAILGUN_HOST pointing at the wrong region (EU vs US); TLS/DNS/network failure; rate limiting from Mailgun.
Common situations: Rotated the API key in the Mailgun dashboard without updating env; domain not verified in Mailgun; recipient on a suppression/bounce list; EU account using the default api.mailgun.net host.
Related errors
- [retrieveRun] Failed to retrieve run data:
- Mailgun API key and domain are required
- Download failed: ${response.status} ${response.statusText}
- Unexpected response from server; Status: ${response.status}
- Request failed with status ${response.status}: ${json.error.
AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12).
Data as JSON: /api/errors/62b64c2a178bba38.
Report an issue: GitHub.