Two dot releases on the same day is the tell. When a WooCommerce version ships one hotfix and then, a few hours later, ships another, someone at the retreat is not sleeping. That is what happened on July 3, 2026 with 10.9.2 and then 10.9.3, and it is worth being precise about why.
I have been through enough Woo minor cycles to recognize the pattern. A feature-frozen branch ships. Real merchants push real traffic through it. Something that only surfaces under production load — a filter returning the wrong shape, a class not autoloading on a warm opcache, a table that quietly diverged — starts showing up in support queues. The team backports, cuts, releases. And then you, the agency, get to decide whether to trust the auto-update button or read the diff first.
For 10.9.3 the answer is clear: apply it. The failure surface is your checkout screen. But before you click, know exactly what changed and why the shape of the fix matters for anyone hooking into woocommerce_mail_callback.
What is actually new
WooCommerce 10.9.3, announced July 3, 2026 by Brian Coords on the WooCommerce Developer Blog, is a same-day follow-up to 10.9.2. It is not a security release. It does require a database update. The release note is a single line: fix a fatal error in WC_Email::send_notification() when a filtered mail callback returns a non-bool value.
The underlying bug is issue #66100, filed June 29, 2026 by bashfoo. Reproduction: enable HPOS, put HPOS and legacy tables in a state where they are out of sync, open any order, change status to Completed, save. WordPress serves the critical error screen. The stack trace lands on class-wc-email.php:1178. The root cause is a strict return type. WC_Email::send_notification() is declared : bool. Its underlying send() method was returning the raw output of the filterable woocommerce_mail_callback without coercing it. On PHP 8 with strict types, that is a hard TypeError. On the wrong day, that is a checkout that does not complete.
The fix ships as PR #66219, a backport to the release/10.9 branch of the trunk fix #66186. Both were merged July 2, 2026. The change is small and correct: cast the mail callback return value to bool before returning from send(), and cast again before firing the woocommerce_email_sent action so the action payload is the same shape either way. Anuj-Rathore24 authored the trunk PR, and the backport went in as a feature-freeze exception with tests for the null-return case.
The four issue tickets closed by the PR (#66100, #66143, WOOPLUG-6935, WOOPLUG-6948) all trace back to one class of trigger: something in the chain that hooks woocommerce_mail_callback and hands back null instead of true or false. The bug report names Gravity SMTP as one reproducer; in practice any transactional mail plugin or in-house filter that shortcircuits wp_mail() without returning a clean boolean can hit it.
Context worth pinning: 10.9.2 shipped a few hours earlier the same day (July 3) to prevent update-time fatal errors from the new settings SDK autoloader and to always-enable enhanced push notifications. 10.9.3 is a separate line item on top. Take both.
Why it matters for WordPress and WooCommerce people
The failure mode is the whole story. This is not a background job that silently retries. This is not a warning in a log. When send_notification() throws inside the order-status transition, whatever triggered that transition — a customer clicking Place Order, a payment gateway webhook, a bulk admin action — gets a critical error page instead of a completed order. On a store doing volume, that is refunds, chargebacks, and support tickets in the same hour.
Two structural takeaways for the way we build.
First: strict return types in core methods are a contract with the whole plugin ecosystem. When Woo added : bool to send_notification() in 10.9.0, every SMTP plugin, every custom mail router, every mu-plugin filter got a new obligation. Most of them return a proper boolean. Some do not. The mature response is exactly what 10.9.3 does — coerce at the boundary rather than assume upstream discipline. It is boring, defensive PHP, and it is right.
Second: HPOS and legacy tables drifting out of sync is now a first-class production condition. It is not a corner case for stores that skipped the sync tool. It is what happens when a plugin writes to the wrong table, when a migration is interrupted, when a staging restore lands on prod. The HPOS documentation already tells you to run the sync checker. The lesson from 10.9.3 is that core is now hardening against the case where you did not.
What I would do (or not do) about it
Apply 10.9.3 on every store running 10.9.0, 10.9.1, or 10.9.2. Skip 10.9.2 in your mental model — 10.9.3 supersedes it. Both migrations run in sequence on upgrade; the database update takes seconds. This is a routine patch and every hour on 10.9.0 with checkout email filters in play is an hour of avoidable risk.
Audit any custom code that hooks woocommerce_mail_callback. The most common shape I see in the wild is a filter that logs, then falls through to wp_mail() — and the fall-through path returns nothing. That is null. Before 10.9.3 that was benign because Woo passed the value through untouched. After 10.9.3 the coercion in core saves you, but you still want the filter to return an explicit boolean. Explicit is cheap. Implicit is a debugging session at 3 AM.
If you run any transactional mail plugin — the WordPress SMTP names you know, Gravity SMTP included — check its version. Update to current. The bug report singled out Gravity SMTP specifically, not because it is broken but because the way it wraps wp_mail() made the coercion gap visible earliest. That will keep happening across the ecosystem until every mail plugin ships a version whose woocommerce_mail_callback handler returns a real boolean.
Do not delay HPOS sync work waiting for another patch. Run the sync tool this week if you have not this quarter. Woo is going to keep hardening the HPOS code path against divergence, but the point of the sync tool is to remove the divergence, not just survive it. And with WooCommerce 11.0 arriving July 28, 2026 with product object caching on by default for new stores and Action Scheduler 4.0.0 landing at the same time, the next set of bugs will look nothing like this one — but they will surface the same way, in production, under real load.
Two dot releases on the same day is the boring part of a healthy project. The problem showed up on a Monday, the fix was in trunk by Wednesday, and stable users had it by Friday. That is the WooCommerce team doing exactly what a serious platform is supposed to do — and it is the argument I keep making for building on top of it.
Last modified: July 6, 2026
United States / English
Slovensko / Slovenčina
Canada / Français
Türkiye / Türkçe