danny-avila/LibreChat · error · Error

Mailgun API key and domain are required

Error message

Mailgun API key and domain are required

What it means

Thrown by sendEmailViaMailgun before any network call when process.env.MAILGUN_API_KEY or process.env.MAILGUN_DOMAIN is unset. The function reads both at call time and refuses to proceed without them, since constructing the Basic-Auth header and the POST URL requires both values. It is a configuration guard, not an upstream failure.

Source

Thrown at api/server/utils/sendEmail.js:27

/**
 * Sends an email using Mailgun API.
 *
 * @async
 * @function sendEmailViaMailgun
 * @param {Object} params - The parameters for sending the email.
 * @param {string} params.to - The recipient's email address.
 * @param {string} params.from - The sender's email address.
 * @param {string} params.subject - The subject of the email.
 * @param {string} params.html - The HTML content of the email.
 * @returns {Promise<Object>} - A promise that resolves to the response from Mailgun API.
 */
const sendEmailViaMailgun = async ({ to, from, subject, html }) => {
  const mailgunApiKey = process.env.MAILGUN_API_KEY;
  const mailgunDomain = process.env.MAILGUN_DOMAIN;
  const mailgunHost = process.env.MAILGUN_HOST || 'https://api.mailgun.net';

  if (!mailgunApiKey || !mailgunDomain) {
    throw new Error('Mailgun API key and domain are required');
  }

  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;

View on GitHub (pinned to 5ff282f900)

Solutions

  1. Set MAILGUN_API_KEY and MAILGUN_DOMAIN in the environment (and MAILGUN_HOST if using a non-default/EU host) and restart the process.
  2. If you intend to use SMTP instead, disable/route away from the Mailgun code path.
  3. Verify the variables are actually loaded by the process (print Object.keys(process.env).filter(k => k.startsWith('MAILGUN')) in a health check).

Example fix

// before: env missing -> throw at call time
// after: fail fast at startup with a clear message
if (process.env.EMAIL_SERVICE === 'mailgun') {
  if (!process.env.MAILGUN_API_KEY || !process.env.MAILGUN_DOMAIN) {
    throw new Error('MAILGUN_API_KEY and MAILGUN_DOMAIN must be set when EMAIL_SERVICE=mailgun');
  }
}
Defensive patterns

Strategy: validation

Validate before calling

const mailgunApiKey = process.env.MAILGUN_API_KEY;
const mailgunDomain = process.env.MAILGUN_DOMAIN;
if (!mailgunApiKey || !mailgunDomain) {
  throw new Error('Mailgun API key and domain are required');
}

Type guard

const hasMailgunConfig = () => !!process.env.MAILGUN_API_KEY && !!process.env.MAILGUN_DOMAIN;

Try / catch

try {
  await sendEmailViaMailgun({ to, from, subject, html });
} catch (err) {
  if (err.message === 'Mailgun API key and domain are required') {
    return res.status(503).json({ message: 'Email service not configured.' });
  }
  throw err;
}

Prevention

When it happens

Trigger: Email subsystem invoked (e.g. password reset, invite) in an environment where Mailgun env vars were never set; MAILGUN_API_KEY present but MAILGUN_DOMAIN missing (or vice versa); env file not loaded in the running process; deploying with a different email provider while Mailgun code paths are still active.

Common situations: New deploy missing secrets; switching from SMTP to Mailgun without provisioning credentials; CI environment without email env vars; a typo in the env var name.

Related errors


AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12). Data as JSON: /api/errors/88b4ca95103e904b. Report an issue: GitHub.