Troubleshooting
A campaign that says Active and sends nothing, saves that are refused, an empty recipient picker, blank emails, mass failures and duplicate sends — the cause of each and the fix.
Almost every MailWizard problem is silent. The screens report success, the campaign says Active, and nothing arrives. Work through this page in the order given — the first two sections cover most of it.
A campaign is Active and nothing is sending
Check these in order. Each one is a complete explanation on its own.
Campaigns are sent by a scheduled job and by nothing else. If the cron PM2 app
is stopped or crashed, every campaign sits at ACTIVE and 0% indefinitely, and
no MailWizard screen says so.
Check pm2 list for a cron entry in online status, then pm2 logs cron. The
Campaigns screen has one indicator: Stalled Sends counts ACTIVE campaigns
untouched for over an hour. On a healthy install that is zero. See
Processes and ports.
The job runs every sixty minutes. Setting a campaign to Active makes it eligible;
it does not send anything at that moment. Trigger processMailwizardCampaigns
manually at /admin/system/cron if you need it now.
An ACTIVE campaign with an empty list stays ACTIVE forever and sends nothing.
The campaign view dialog badges this as No recipients, and the send job logs
"No targets found".
This normally means the recipient list was lost on save rather than never added — see the saving is refused section below.
Disabling mailwizard at /admin/system/extension deregisters the send job
within about a minute. Campaign rows are untouched, so an ACTIVE campaign
simply stops progressing with no visible change.
The campaign page shows the status it last wrote, and it does not poll. Reload
the Campaigns list and read the status from the table instead. A campaign the
send job paused itself — for an unreadable recipient list or a speed below 1 —
shows as PAUSED there.
The job only looks at PENDING recipients. A campaign whose list is entirely
resolved should have flipped to COMPLETED; if it is still ACTIVE, the
completion write failed and the cron log will say so.
The send job paused my campaign by itself
The job pauses a campaign rather than retrying it forever in two situations, because a visible pause beats invisible failure. Both write the reason to the cron log.
| Reason | Fix |
|---|---|
| The recipient list cannot be read as a list | Re-add the recipients and reactivate. A list written by hand or by an old build is the usual cause |
| The sending speed is not a positive number | Set Emails per hour to at least 1 and reactivate |
Neither can happen through the current admin screens — both write paths validate now — but rows written before that validation existed are still out there.
Saving a campaign is refused
The recipient list is capped at 10,000 characters of stored JSON. Recipients ticked in the picker carry their entire user profile, so this is reached at roughly fifteen of them.
It blocks editing as well as creating: an existing campaign holding a longer list cannot be saved at all, not even to correct its name. The recipients already stored keep sending; only saving is blocked.
Fix: split the audience across several campaigns, or write the list through
PUT /api/admin/mailwizard/campaign/{id}/target, which has no size cap. See the
API reference.
Other refusals and what they mean:
| Message | Cause |
|---|---|
targets[0] is missing an email address |
Almost always Select all in the recipient picker — it adds recipients with blank addresses. Tick recipients individually |
targets must be a JSON array of recipients |
The list was posted as something other than an array of objects |
speed must be a whole number of emails per hourly send run, at least 1 |
The throttle is below 1 or not an integer |
Template not found |
The template this campaign points at has been deleted. Pick another one |
name is required / subject is required |
Blank, or whitespace only |
The recipient picker is empty
Three causes, in order of likelihood.
The search matches first name only. The box filters on firstName with a
starts-with match. An email address, a surname, or a name fragment from the middle
returns nothing — which looks exactly like the user not existing. Search by the
beginning of the first name, or page through the list.
The role lacks view.user. The picker reads the platform's user list through
a core CRM endpoint gated by view.user, not by any MailWizard permission. A role
with every MailWizard key and no view.user opens an empty dialog silently.
Grant it at /admin/crm/role.
There genuinely are no active users. The lightweight list behind Select all returns active accounts only.
Every recipient failed
Read the error on a failed recipient card — the send job records the mail transport's own message there, along with the attempt count.
535, "Username and Password not accepted", "Invalid login". The transport
credentials in .env are wrong or the provider has revoked them. Gmail and
Outlook both require an app password, not the account password.
Verify by triggering any transactional email from the platform — a password reset to your own address. If that fails too, the problem is platform-wide, not MailWizard.
Almost always a port and encryption mismatch. The client treats the connection as
secure when the port is 465 or APP_NODEMAILER_SMTP_ENCRYPTION is ssl —
and ssl is the default. So leaving the encryption unset while using port 587
negotiates SSL against a STARTTLS port. Pair 587 with tls and 465 with
ssl.
421, 454 4.7.0 Too many login attempts, "rate limit exceeded". Your provider
is refusing the volume. Lower Emails per hour and retry the failures later.
Note that the throttle is per campaign: three campaigns at 100 each are 300 emails an hour against one account.
A 500 with this message means APP_EMAILER holds a value that is not one of
local, nodemailer-service, nodemailer-smtp or nodemailer-sendgrid. Note
that deleting the variable entirely does not fall back to what .env.example
ships — the code's own default is nodemailer-service.
That is the circuit breaker working. Five consecutive failures are treated as a
provider outage rather than five bad addresses, so sending stops for the rest of
the run and the untouched recipients stay PENDING. Fix the transport; the next
hourly run resumes.
Recipients received a blank email
The template body is empty. The Templates list flags this with a No content badge on the row.
If the template was created with the visual editor on a build before v6.1.0, the
create flow discarded the design and stored a literal {} while reporting
success. Editing always saved correctly, so opening the template and saving it
once repairs it. Check every template you created that way after upgrading.
Recipients received the email twice
Two causes, both operator actions.
Stop was used instead of Pause. Stop resets every recipient to PENDING, so
restarting emails everybody again. The interface confirms first and names how
many people that is. Use Pause to halt a send, and Retry failed to pick up
only the failures.
A running campaign was saved from the campaign page. That page posts the
whole recipient list back as it was when the page loaded, overwriting anything
the send job has written since. Recipients already delivered to revert to
PENDING and are emailed again on the next run. Pause the campaign, reload the
page, edit, then start it.
The template editor has no Blocks panel
Three possibilities:
- You have no saved blocks. The panel is hidden rather than shown empty, on purpose. Save one block and it appears next time.
- The block you just saved is not there yet. The block list is read once when the editor initialises. The success toast offers a Reload editor action that re-initialises with the new block and carries your current design across.
- The role lacks
view.mailwizard.block. No panel, no explanation. Grant it alongside the template permissions.
Opening a template shows a blank canvas
The stored design is not something the editor can read — usually because the template was imported as raw HTML. The Templates list badges this as HTML only.
The template still sends correctly; it simply cannot be edited visually. Treat it as read-only and re-import the whole thing when it needs to change.
A rename did not stick
Template names are write-once. The editor's name field is editable and posts the value, but the update endpoint writes the body and design only. There is no other rename path. Create a new template with the right name and repoint the campaigns that use it.
Campaign names can be changed normally, through the campaign page.
Emails are arriving in spam
Nothing in MailWizard influences this. The checks worth running, in order:
- SPF, DKIM and DMARC published for the domain in your From address
- DKIM configured at the provider — the
APP_NODEMAILER_DKIM_*variables only apply to thelocalsendmail transport - A sending volume your domain's reputation supports; a new domain emitting a thousand messages in an hour looks compromised
- An unsubscribe link in the template — nothing adds one for you
- Images hosted on your own domain rather than inlined as base64
- Addresses that actually exist; repeated delivery to dead mailboxes is a strong negative signal
Remember the reporting ceiling: SENT means your mail transport accepted the
message. It does not mean it was delivered, and it certainly does not mean it
reached an inbox. There is no open or click tracking in this product, so
inbox placement can only be measured from your provider's dashboard or by seeding
test addresses.
Staging sent real email to real customers
MAIL_DISABLED=true stops the platform's queued and templated email. It does
not stop MailWizard, whose send job calls the mailer directly and never
consults that flag.
Before restoring a production database onto any non-production box, do one of:
- Disable the
mailwizardextension at/admin/system/extension, or - Set every campaign to
PENDINGorCANCELLED, or - Point
APP_EMAILERat a transport that cannot reach the internet.
Where to look next
| Symptom | Page |
|---|---|
| Nothing sends, cron suspected | Processes and ports |
| Mail fails platform-wide | Core troubleshooting |
| Transport variables | Environment variables |
| Permissions and roles | Roles and permissions |
| How the send loop behaves | How sending works |