WooCommerce Order Email Not Sending After Checkout: Fix

WooCommerce order confirmation email not sending after checkout? Fix the failed action scheduler, SMTP fallback, and the email template hook with code.
WooCommerce Order Email Not Sending After Checkout: Fix
WooCommerceWordPressEmail
April 29, 20266 min read1089 words

The Problem

A client called me on Sunday: their store was processing orders fine, Stripe was charging cards, the order showed up in the WooCommerce dashboard, but the customer never got the "Your order is confirmed" email. The admin "New order" email also never arrived. The site was on WooCommerce 10.8, WordPress 6.9, and Action Scheduler had several thousand pending entries piling up.

If you are seeing the same thing — orders go through, money lands in Stripe, but no transactional email reaches the customer or the store admin — the cause is almost always one of three things: Action Scheduler is stalled, the host blocks wp_mail() outbound, or a recent plugin update detached the woocommerce_order_status_processing_notification hook from the email class.

I have hit all three this year on different stores. Here is how I diagnose it in under ten minutes and what to actually patch.

Why It Happens

Since WooCommerce 8.0 every transactional email is dispatched asynchronously through Action Scheduler. When a customer checks out, WooCommerce does not call WC_Email::send() directly anymore. It enqueues a job called woocommerce_send_email_for_order that runs on the next WP-Cron tick. If Action Scheduler is broken, the job sits in wp_actionscheduler_actions forever and no email goes out.

Action Scheduler stalls for two common reasons. First, WP-Cron is disabled (DISABLE_WP_CRON in wp-config.php) and no replacement system cron is running, so the queue never processes. Second, a single failing job throws a fatal and Action Scheduler quarantines the whole batch — one bad PDF invoice plugin can poison the queue.

The second class of failures is the host. Most managed WordPress hosts (WP Engine, Kinsta, SiteGround) block raw wp_mail() to anywhere except their internal relay. If you have not configured an SMTP plugin, wp_mail() returns true from PHP but the message is silently dropped before it leaves the server. Cloudflare Email Routing on the receiving domain will also bounce the From address if the SPF record does not include the sending host.

The third one bit me last month. WooCommerce 10.7 refactored WC_Emails::init_transactional_emails() and a popular order-status plugin still hooks the old action name. The plugin's filter ran first and removed WC_Email_Customer_Processing_Order from the active emails array. The dashboard preview still rendered the template, but the production hook was gone.

The Fix

Step 1: Confirm whether the email is being scheduled at all. Place a test order, then run this in WP-CLI:

wp action-scheduler list --hook=woocommerce_send_email_for_order --status=pending --format=table

If you see a row with your order ID, scheduling works and the issue is the queue or the mail transport. If the table is empty, the email was never queued — the hook is broken and you skip to Step 4.

Step 2: Force-process the queue and read the logs. Run:

wp action-scheduler run --batch-size=10

Then check the log for the action with:

wp action-scheduler list --hook=woocommerce_send_email_for_order --status=failed

A failed status with a stack trace pointing at a third-party plugin (PDF Invoices, Sequential Order Numbers, Mailchimp for WooCommerce) is your culprit. Deactivate that plugin, re-queue the action, and the email goes out.

Step 3: Replace WP-Cron with a real cron job. This is the single biggest fix on managed hosts. In wp-config.php:

define( 'DISABLE_WP_CRON', true );

Then add a system cron entry that hits the real endpoint every minute:

* * * * * curl -s https://your-store.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1

WP-Cron only runs when a visitor hits the site, which on a low-traffic store means Action Scheduler waits hours between ticks. A real cron job processes the queue every minute and emails go out immediately after checkout.

Step 4: Patch the missing email class hook. If Step 1 showed the action was never queued, the email class is detached. Drop this into a tiny mu-plugin at wp-content/mu-plugins/wc-email-restore.php:

<?php
/**
 * Re-attach the WooCommerce processing-order email if a plugin removed it.
 */
add_filter( 'woocommerce_email_classes', function ( $emails ) {
    if ( ! isset( $emails['WC_Email_Customer_Processing_Order'] ) ) {
        $emails['WC_Email_Customer_Processing_Order'] = new WC_Email_Customer_Processing_Order();
    }
    if ( ! isset( $emails['WC_Email_New_Order'] ) ) {
        $emails['WC_Email_New_Order'] = new WC_Email_New_Order();
    }
    return $emails;
}, 99 );

Priority 99 runs after every other plugin filter, so you win the last write. Confirm the email is back by visiting WooCommerce → Settings → Emails and checking that "Processing order" and "New order" both have a green status.

Step 5: Stop relying on wp_mail() and route through SMTP. Install WP Mail SMTP, FluentSMTP, or Post SMTP, point it at a real provider (Postmark, Resend, Amazon SES), and verify SPF/DKIM on the sending domain. Then send a test from Tools → Site Health → Info → wp_mail. The provider's dashboard will show the message; if it does not, your host is still blocking the outbound port and you need to use the provider's HTTP API instead of SMTP. The WooCommerce email troubleshooting docs cover the full deliverability checklist if you want to dig further.

Step 6: Set the From address to a domain you control. This catches the last 10% of "emails go out but land in spam" cases. In WooCommerce → Settings → Emails → Email sender options, set the From email to something on your store domain (orders@your-store.com), not a free Gmail or Yahoo address. Free-mail senders fail DMARC and most receiving servers either reject or junk them.

The Lesson

WooCommerce order email failures are almost never the email template — they are Action Scheduler stalled, WP-Cron not firing, or wp_mail() getting eaten by the host. Run the WP-CLI queue inspection first, replace WP-Cron with a real cron, and route through a real SMTP provider with a domain From address. That covers about 95% of cases I see.

If your store is currently dropping order confirmations and losing customers to "did my order go through?" support tickets, this is exactly the kind of thing I fix on a same-day basis — see my services. If your store also has the cart spinning forever before the email step, I wrote up the WooCommerce Add to Cart AJAX fix last week which often pairs with this issue.

Back to blogStart a project