---
title: "Mailgun — Wrong Sending Domain Causes Silent 404/401 → Generic 500"
aliases: [mailgun-domain, mailgun-sending-domain, mailgun-silent-failure]
tags: [mailgun, email, gotcha, silent-failure, oliver-agency]
created: 2026-05-15
updated: 2026-05-15
---

# Mailgun — Wrong Sending Domain Causes Silent 404/401 → Generic 500

## The Gotcha

The Mailgun **sending domain** must be the verified subdomain, not the root domain:

| Wrong | Correct |
|-------|---------|
| `oliver.solutions` | `mg.oliver.solutions` |

Using the root domain returns HTTP 404 or 401 from the Mailgun API.

## Why It's Silent

The Oliver Agency Python Mailgun wrapper catches the non-200 response and returns `False` instead of raising. The calling code checks `if result:` — `False` is falsy, so it returns early with no log entry and no exception. The endpoint then reaches a generic `500 Internal Server Error` with no diagnostic message.

```python
# Python wrapper silently returns False on 404/401
result = send_email(to=..., subject=..., body=...)
if not result:
    return  # ← silent early exit, no logging
# code below never runs → unhandled state → 500
```

## The Fix

1. Always use `mg.oliver.solutions` (or the project-specific verified subdomain) as `MAILGUN_DOMAIN` in `.env`
2. Add explicit error logging in the wrapper or the caller — `False` return should at minimum log `WARNING`
3. When debugging: check Mailgun dashboard → Sending → Logs for the domain — 404/401 errors appear there even when the Python side swallows them

## Where Credentials Live

```bash
source ~/.secrets/mailgun.env
```

Variables: `MAILGUN_API_KEY`, `MAILGUN_DOMAIN` (should be `mg.oliver.solutions`).

Base URL: `https://api.mailgun.net/v3`

## Related

- [[wiki/concepts/silent-exception-swallow-falsy-guard|silent-exception-swallow-falsy-guard]] — Double silent failure pattern (exception swallow + falsy guard)
