Contact Form 7 “error trying to send your message” when the SMTP test works

A form on the site answers “There was an error trying to send your message. Please try again later.” in a red box, while the SMTP plugin’s test email arrives without complaint. Both results are accurate. The test proves WordPress can send one particular message: the plugin’s own sender, to one recipient the site owner chose. The form’s message can differ in two ways that matter, its sender and its recipients, and one setting in the SMTP plugin decides which to suspect first. With WP Mail SMTP’s Force From Email on, the plugin gives the form’s message the same sender as the test, so the sender is unlikely to be the difference. With it off, the form’s own From field is the first suspect. The reason for the failure is usually in a mail log, but not always: if another plugin stopped the send before it reached the mail library, there is no server reply to read. And a logged error is evidence about which field caused the failure, not proof.

What the red message means, and what it rules out

Contact Form 7 uses this sentence as the default text for two outcomes, and the border colour tells them apart. With an orange border it is the spam message: a spam check (Akismet, a CAPTCHA, the disallowed-keys list) rejected the submission and nothing was sent. With a red border it is the mail_sent_ng message, “Sender’s message failed to send” in the form’s Messages tab, and the send itself failed. Validation and acceptance errors get yellow and their own wording.

In CF7 6.2.4.1’s includes/submission.php, validation, the acceptance checkbox, the spam check and file uploads all run first. Only after they pass does CF7 hand the form’s Mail template to WordPress’s wp_mail(); WPCF7_Mail::send() returns whatever wp_mail() returns. A false sets the mail_failed status and the mail_sent_ng message, and CF7’s front-end script turns that status into a failed class, which the stylesheet draws in red. The only other red status, aborted, carries different wording.

That makes this a send failure, not a spam-folder problem. The same code rules out one common suspect and makes another unlikely:

  • Mail (2), the optional auto-reply to the visitor, cannot cause it. CF7 sends Mail (2) only after the main Mail has succeeded and does not check its result. If Mail (2) fails, the visitor still sees the green success message.
  • Cloudflare or a security plugin is unlikely in the usual case. Either can block the request the form makes to CF7’s REST endpoint, and when the block comes back outside the 200 range (a Cloudflare challenge page, a firewall’s 403), or as something other than JSON, CF7’s front-end script throws an error before it writes any message. The box stays empty and the form stays in its submitting state. A proxy or security plugin can rewrite responses in other ways, though, so check what actually came back rather than assume. In the browser’s Network tab, a normal CF7 send failure is the feedback request returning 200 with "status":"mail_failed" in the body.

Why the test passes and the form fails

WP Mail SMTP‘s test email calls wp_mail() with the recipient typed into the Email Test tab and no From header of its own. WordPress then falls back to its default sender, wordpress@ plus the site’s domain, and WP Mail SMTP’s wp_mail_from filter swaps any default sender for the From Email in its settings. The test therefore always goes out from the From Email configured in WP Mail SMTP, to an address the site owner chose.

A CF7 form builds a different message. Its sender comes from the From field on the Mail tab, its recipients from the To field, and anything typed into Additional Headers goes along too. Whether WP Mail SMTP replaces that From depends on one setting, Force From Email:

  • On, which is the default for a fresh install in 4.10.3 (src/Options.php), the plugin’s wp_mail_from filter replaces whatever sender the form set with its From Email, so the form and the test normally leave from the same address. The form’s original sender moves to Reply-To when the form has not set one.
  • Off, the plugin replaces only WordPress’s default sender and leaves any other address alone. CF7’s out-of-the-box From is [_site_title] <wordpress@yourdomain>, or the admin email when that is on the site’s domain (includes/contact-form-template.php). The wordpress@ version is exactly WordPress’s default, so the plugin replaces it. The admin-email version goes out as written, and so does any edited From, whether that is [your-email] (set so replies reach the visitor) or an address on another domain.

A From: line typed into Additional Headers counts as an edited From. CF7 adds those headers after its own From line, and WordPress’s wp_mail() keeps the last From it reads when it parses the headers. With Force From Email off, the extra line decides the sender without showing up in the From field.

On a default install, then, look at the form’s recipients and the logged error before the sender: WP Mail SMTP gives the form the test’s From, so the sender is unlikely to be what differs. Custom code or another plugin hooked into the same filters can still change the sender, so check the From on the failed entry in the log. The sender becomes the suspect only when Force From Email has been unticked, or on a mailer that does not force it.

When the form’s own sender does reach the mail service, what the service does with an address the login account does not own depends on the service and the sending path:

  • Microsoft 365, when the plugin logs in to Exchange Online with SMTP AUTH (username and password): unless that mailbox has Send As permission (the right to send under another address) for the From address, the message is rejected with a permission error (Microsoft’s SMTP submission guide). Repeated failures can then get the account throttled for SMTP AUTH with 550 5.2.252 (continuous-error throttling), and at that point the test email fails too.
  • SendGrid refuses a From that is neither a verified single sender nor on an authenticated domain, with “The from address does not match a verified Sender Identity” (SendGrid support).
  • Other services may refuse such a sender, rewrite it, or accept it; check the service’s own sender rules.

CF7 flags the risky case in its own editor. When the From field resolves to an address outside the site’s domain, the Mail tab shows “Sender email address does not belong to the site domain.” The warning is based on the form’s settings alone, so whether the mail service accepts that address is a separate check.

The other field that can fail a send is To. CF7’s editor flags a To field with invalid syntax when the form is saved, but a mail-tag’s value is only known when someone submits. If it resolves to nothing or to an address PHPMailer rejects, wp_mail() skips that address; with no valid recipient left, PHPMailer refuses the send with an error about having no recipient. A mail service can also refuse a real recipient, which the test never exercises because the site owner picked the test’s recipient.

The test email carries no attachment, but a form with a file field sends whatever the visitor uploads. CF7 filters out attachments that sit outside wp-content, cannot be read, or would take the mail’s total above 25 MB, and raises a PHP notice for each file it drops. An attachment CF7 keeps can still be refused by the mail service, which applies its own size and content limits. Read the logged error for the failed submission, then submit once without a file: if that one goes through, the attachment is the likely cause. Repeat the pair, with and without the file and nothing else changed, before you treat it as settled.

Read the real error instead of guessing

When PHPMailer throws an exception during wp_mail(), WordPress fires the wp_mail_failed action with a WP_Error holding the exception message and the mail data. For an SMTP refusal, that message usually includes the server’s reply, though not the whole SMTP conversation. Not every failed send reaches PHPMailer: a plugin can take over wp_mail() through the pre_wp_mail filter and return false before PHPMailer runs, and then the hook never fires. The logs below rely on the hook, so an empty error on a failed send points at whatever took over wp_mail(). Submit the form once, then look in one of these:

  • WP Mail Logging. It logs each wp_mail() call and saves the wp_mail_failed error message on that call’s entry. When the hook fires, the failed submission’s entry holds the reason.
  • WP Mail SMTP, free version. WP Mail SMTP > Tools > Debug Events keeps send errors with the “Debug Email Sending” option off; that option gates only the extra debug-level events. The per-message email log is a Pro feature; Debug Events is not.
  • FluentSMTP. Its free email log lists failed messages with the server response (FluentSMTP docs).
  • No plugin. A few lines in a must-use plugin write the hook’s error to the PHP error log. Like the plugins above, they catch only failures that fire the hook. The log can hold visitor addresses, so keep it out of public directories and remove the snippet once the fault is found.
<?php
add_action( 'wp_mail_failed', function ( $error ) {
	error_log( 'wp_mail_failed: ' . $error->get_error_message() );
} );

The wp_mail_failed guide covers what the hook catches and what it misses, and the SMTP error decoder translates server replies. Compare the From in the failed entry with the test’s From: if they differ, the sender is the lead suspect. An error that names the sender (“send as denied”, “verified Sender Identity”, “sender address rejected”) points at From; one that explicitly names a recipient points at To. Many errors name neither: a dropped connection, a timeout, a policy refusal, or a bare “could not send”. Don’t pin those on a field. Even an error that does name one narrows the search rather than settling it, and the same goes for the recorded headers. Check the From and To in the entry’s headers against what the mail account allows, and take the exact error text to the mail service’s documentation.

The fix, and how to re-test

  1. If the error names the sender, check that Force From Email is ticked in WP Mail SMTP (FluentSMTP has a per-connection equivalent). For messages that pass through WP Mail SMTP’s wp_mail() filter, the plugin then uses its configured From Email, whatever the form’s From field says. Another plugin or custom code that changes the sender after WP Mail SMTP, or mail sent outside wp_mail(), can still go out from a different address. If Force From is already ticked and the error still names the sender, the configured From Email is likely the address being refused. The test uses the same From, so a refusal that applies to both messages should fail the test too. If the test still succeeds, compare the From and the connection recorded on the failed form entry with the configured ones: routing, other plugins’ hooks, or a policy that looks at the whole message can make the two sends differ.
  2. On a mailer without that setting, or to keep the form’s sender explicit, set the form’s From to an address on the site’s domain that the mail account may send as, and remove any From: line from Additional Headers. On Microsoft 365, use the login mailbox’s own address or grant that mailbox Send As on the one you want; sending from a Microsoft 365 alias covers that case.
  3. Put the visitor’s address where replies need it: Reply-To: [your-email] in Additional Headers. That is CF7’s default for new forms, and the Reply-To guide shows the full setup.
  4. If the error names a recipient, fix the To field: a fixed address, or a mail-tag that always holds a valid address.
  5. Re-test by submitting the form, not by sending the plugin’s test email. The test never exercised the form’s From, To or headers; the form does.

If the log shows a different error, the cause is elsewhere, and the general WordPress email delivery answer is the next stop. The From address explainer covers how WordPress picks a sender when no plugin intervenes.

Read from the source code in November 2026; the claims were not checked with test sends. Contact Form 7 6.2.4.1: submission.php (WPCF7_Submission::mail(), mail_failed status), mail.php (WPCF7_Mail::send(), compose()), contact-form-template.php (from_email()), config-validator/mail.php (validate_mail_sender(), validate_mail_recipient()), rest-api.php (create_feedback()), js/index.js (status-to-class map, fetch wrapper), css/styles.css (.wpcf7 form.failed, form.spam). WP Mail SMTP 4.10.3: Options.php (from_email_force default), Processor.php (filter_mail_from_email(), allow_setting_original_from_email_to_reply_to()), TestEmail.php (send()), DebugEvents.php (add()). WP Mail Logging 1.17.1: WPML_Plugin.php (log_email_failed()). WordPress 7.1.4: pluggable.php (wp_mail() header parsing, pre_wp_mail short-circuit, wp_mail_failed). FluentSMTP: email logs documentation.