That WooCommerce decimal change that’s silently breaking stores.

Got a call from a long-time client last week. Absolute panic. They’d just run a routine batch of plugin updates, and suddenly their shipping calculator was failing. Just… blank. No rates, no error message, nothing. And of course, it was only happening for certain shipping classes, so they didn’t catch it in testing. Customers were abandoning carts left and right. A real mess.

My first thought was a conflict. Or maybe a misconfigured shipping zone. I spent a solid half-hour ruling out the obvious stuff. Then I popped open the browser’s developer tools, expecting to see a JavaScript error. Nothing. The AJAX request was firing, but the response was just empty. That’s when you know you’re in for a fun afternoon. This wasn’t a simple settings tweak; this was a code problem. The kind of problem caused by a subtle `wc_format_decimal` change.

The Silent Killer: WooCommerce’s `wc_format_decimal` Change

After digging through their custom shipping plugin, I found the culprit. It was a single line of code that took a value, passed it through wc_format_decimal, and expected a number. The problem is, sometimes that initial value was an empty string. For years, passing an empty string with a precision set to 0—like wc_format_decimal( '', 0 )—would spit back a numeric 0. But not anymore.

As of WooCommerce 10.4, that same function call now returns an empty string. Total nightmare if your code uses a loose comparison (==) or expects a float for calculations. The official advisory on the WooCommerce Developer Blog explains this is for consistency, which, as a developer, I get. It’s technically the *correct* behavior. But in the real world, this kind of subtle change can bring a store to its knees. It’s a silent failure, the worst kind of bug.

My client’s plugin was checking if the value was numeric, and an empty string, of course, isn’t. The entire calculation failed right there.

Here’s How You Fix It. The Right Way.

The quick fix might seem to be just casting the result to a float. Don’t do it. You should always sanitize your inputs *before* they hit the function. Never trust the type of data you’re dealing with. The better, more defensive way to code this is to check the value beforehand. If it’s an empty string, make it a zero. Explicitly.

// Don't just pass the value. Sanitize it first.
$shipping_cost = get_post_meta( $post_id, '_shipping_cost', true );

// If the value could be an empty string, explicitly cast it to a float or int.
$sanitized_cost = ( $shipping_cost === '' ) ? 0.0 : (float) $shipping_cost;

// Now you can safely use the function.
$formatted_cost = wc_format_decimal( $sanitized_cost, 2 );

// Your downstream logic is now safe.
if ( $formatted_cost > 0 ) {
    // Do something...
}

So, What’s the Point?

The lesson here isn’t just about one function. It’s a reminder about how fragile ecommerce systems can be. A single, undocumented change in a core dependency can have a massive ripple effect. Trust me on this, you have to code defensively.

  • Always Sanitize Your Inputs: Never assume a variable is the type you expect it to be. Check it. Cast it. Be deliberate.
  • Use Strict Comparisons: If you’re checking for false or 0 or an empty string, use the triple equals (===). It avoids weird type coercion bugs like this one.
  • Read the Dang Changelogs: I know, I know. Who has the time? But even a quick skim of the “For Developers” section in major updates can save you hours of pain.

Look, this stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.

Leave a Reply

Your email address will not be published. Required fields are marked *