WooCommerceHPOSUpdated for WC 10.7

WooCommerce HPOS Sync Not Working After the 2026 Migration

IN
Reviewed by the Instant Nerds Team|Last updated: May 2026
Quick summary

WooCommerce High-Performance Order Storage broke for thousands of stores during 2025 and 2026 in three distinct ways. Sync that runs forever without writing a single row to wp_wc_orders, almost always caused by PHP 8.4 silently rolling back transactions. UNIQUE-constraint crashes on the order_key column for stores with legacy custom checkout flows. And, since WooCommerce 10.7 shipped on April 14, 2026, custom code that writes orders directly to wp_postmeta no longer auto-syncs to HPOS because sync-on-read was disabled by default. This page walks the actual diagnosis WooCommerce support uses, with real WP-CLI commands and the data-corruption checks most merchants miss.

Key facts at a glance

WooCommerce HPOS sync status in 2026

Last updated

Default since
HPOS became the default order datastore for new WooCommerce installs in late 2023. Existing stores migrate on opt-in. New custom development is expected to target HPOS by 2026.
Most painful recent change
WooCommerce 10.7 shipped on April 14, 2026. It disabled HPOS sync-on-read by default. Custom code or non-HPOS-aware plugins writing directly to wp_postmeta no longer get lazily synced into HPOS. The admin notice was dismissible.
Hallmark sync-stuck symptom
Action Scheduler reports wc_run_batch_process batches complete, but wp_wc_orders and wp_wc_orders_meta stay empty. wp wc hpos count_unmigrated never decreases. No errors anywhere in the logs.
Most common silent cause
PHP 8.4 plus WooCommerce 10.4.x. Silent transaction rollbacks during HPOS batch sync. WooCommerce staff confirmed this in a December 2025 support thread. Switch WP-CLI to PHP 8.2 to surface the real underlying error.
Primary blocking error after the PHP fix
Duplicate entry empty value for key wp_wc_orders.order_key. Legacy orders with empty _order_key cannot be inserted into the HPOS table because order_key is UNIQUE NOT NULL. Backfill before retrying.
Distinctive failure mode
Sync looks healthy from the outside. Action Scheduler is processing, batches complete, no errors in logs. Inside, every transaction is being rolled back silently. The progress percentage advances because the migrator counts batches attempted, not rows inserted. Six hours later you check the new tables and they are still empty. The only way to surface the real error is to switch to WP-CLI on PHP 8.2 and watch the sync fail loudly instead of silently.

Source: WooCommerce official documentation, the WooCommerce 10.7 developer advisory from February 2026, GitHub issue tracker for the WooCommerce repository, the WordPress.org support thread documenting a 276,000 order migration, and our hands-on repairs. Get a quote in 60 seconds →

What HPOS is and what changed in 2026

High-Performance Order Storage moves WooCommerce orders out of the generic wp_posts and wp_postmeta tables and into purpose-built tables, primarily wp_wc_orders, wp_wc_orders_meta, wp_wc_order_addresses, and wp_wc_customer_lookup. The new schema uses dedicated columns for status, totals, dates, and customer ID, with NOT NULL and UNIQUE constraints where the data model requires them. For stores with more than about 50,000 orders, the move from a generic key-value meta table to a relational schema with proper indexes drops Orders dashboard load time from tens of seconds to under one. That is the upside.

The downside is migration. HPOS does not tolerate the messy historical data that wp_postmeta happily stored for years. Orders without an _order_key, addresses missing required fields, programmatic orders skipping the standard CRUD path, and custom checkout flows that wrote non-standard meta all fail the new schema constraints. The migrator does not auto-clean. It either crashes or, on PHP 8.4, silently rolls back the transactions without telling you anything.

Critical 2026 dates
  • Late 2023: HPOS becomes the default order datastore for new WooCommerce installations.
  • Throughout 2025: Migration of large existing stores accelerates. PHP 8.4 silent-rollback issue first reported in WooCommerce 10.4.x.
  • February 16, 2026: WooCommerce publishes the developer advisory that sync-on-read will be disabled by default in 10.7.
  • April 14, 2026: WooCommerce 10.7 ships. Stores that auto-updated see the dismissible admin notice. Many dismiss it without action.
  • May 2026 and beyond: Stores discover their custom integrations have been silently writing to wp_postmeta with no HPOS reflection for weeks. Order data appears inconsistent across reports, custom dashboards, and the new Orders list view.

Which symptom matches yours

Find the row that matches your situation. Each one points to a different root cause and a different fix path.

SymptomMost likely root cause
wp wc hpos count_unmigrated reports the same number forever, even after hours of running syncSilent transaction rollbacks. Almost always PHP 8.4 with WooCommerce 10.4.x. The HPOS sync code path is not yet PHP 8.4 compatible. Action Scheduler shows batches complete but no rows commit.
Sync fails with WordPress database error: Duplicate entry empty value for key wp_wc_orders.order_keyLegacy orders have an empty or missing _order_key in wp_postmeta. The wp_wc_orders table has a UNIQUE NOT NULL constraint on order_key, so the second empty value crashes the entire batch. Backfill the meta before retrying.
Migration sync completes but Orders dashboard shows $0 total for every migrated order in the list viewThe total_amount column did not populate during sync, usually because a partial sync ran in an unsupported state, often after manually altering the schema to bypass a constraint. The individual order screen looks fine because it falls back to wp_postmeta.
Custom code or a plugin updates an order but the change is invisible in the HPOS-backed Orders list since April 2026WooCommerce 10.7 (released April 14, 2026) disabled sync on read by default. Direct writes to wp_posts and wp_postmeta no longer lazily back-fill into HPOS. Either re-enable the filter or refactor the code to use the CRUD layer.
Aggregate reports or analytics show customer email or name mismatching the billing address on the same orderwp_wc_customer_lookup data corruption from the original HPOS migration. Legacy WordPress user_id was mapped incorrectly to the new HPOS customer_id. Hidden on single order screens, only visible in joins against the lookup table.
Orders dashboard takes 10+ seconds to load and Query Monitor shows tens of thousands of rows fetched from wp_wc_orders_metaHPOS is running in an unsupported state. Core columns like order_key, total_amount, or status are empty, so WooCommerce falls back to expensive metadata lookups instead of using the indexed columns. Usually a side effect of constraints being dropped to force the original sync.
After HPOS migration, the legacy wp_postmeta order rows are still present and not deletedExpected behavior on partial setups. Legacy data is retained until you explicitly run the cleanup tool. GitHub issue 39656 tracks merchant confusion on this. Do not delete the rows manually until HPOS is verified primary and the data has been re-validated.

The three root causes

The HPOS issues clustering in 2026 fall into three buckets. Each one needs a different fix. Knowing which applies determines whether the work takes 20 minutes or two days.

1. PHP 8.4 silent transaction rollback during HPOS sync

Stores running PHP 8.4 with WooCommerce 10.4.x see HPOS sync that produces no errors and no rows. Action Scheduler completes batches, the Orders setting screen shows the sync progressing, WP-CLI wp wc hpos sync reports success on small chunks, and yet SELECT COUNT(*) FROM wp_wc_orders returns zero. WooCommerce staff confirmed in a December 2025 support thread that HPOS sync relies on long-running batch processes, object hydration, and DB transactions that are not yet fully compatible with PHP 8.4, and the result is silent transaction rollback. Batches report as completed because Action Scheduler counts processing attempts, not committed rows. The workaround is straightforward: switch WP-CLI to PHP 8.2 and run sync from the command line. On 8.2 the sync either succeeds or fails with a visible error that points to the next problem to solve.

2. UNIQUE-constraint crash on order_key for legacy orders

Once the PHP version is right, the next blocking error is almost always WordPress database error: Duplicate entry empty value for key wp_wc_orders.order_key. The new HPOS table defines order_key as UNIQUE NOT NULL. Legacy orders that lack a _order_key in wp_postmeta, common on stores that ran custom checkout flows, accepted orders through a non-standard API, or have very old historical data, all try to insert an empty string. The first empty string inserts fine. The second one violates the unique constraint, the batch crashes, and the migrator gets stuck retrying the same batch. The supported fix is to identify affected orders with a SQL query against wp_postmeta filtered for empty or missing _order_key, backfill unique keys, and only then re-run wp wc hpos sync. Dropping the UNIQUE constraint to force the sync to finish is the path of most regret: it gets the percentage to 100, but the resulting HPOS state is unsupported and the Orders dashboard falls back to expensive metadata scans afterward.

3. WooCommerce 10.7 disabled sync-on-read in April 2026

WooCommerce 10.7, released April 14, 2026, turned off the lazy fallback that previously copied order changes from wp_postmeta into HPOS at read time. Before 10.7, if a custom plugin or your own code called update_post_meta on an order, the change would lazily sync back into HPOS the next time WooCommerce read that order. After 10.7, the change stays in wp_postmeta and is invisible to anything reading from wp_wc_orders_meta, including the Orders list view. The visible signature is, after the auto-update, custom dashboards, third-party integrations, and any code that updates orders directly via the post tables start showing stale or missing data in the new Orders list. The temporary workaround is the woocommerce_hpos_enable_sync_on_read filter returning true. The long-term fix is refactoring the code to use wc_get_order, $order->set_*(), and $order->save() via the CRUD layer, which writes to HPOS natively.

DIY vs hand it off

HPOS issues span from one-filter-fixes to multi-day data-cleanup projects. If the left column matches your situation you can probably resolve this in an afternoon. If the right column matches, get help.

Realistic on your own

  • Your store has under 10,000 orders and the legacy meta is well-formed
  • The issue is the WC 10.7 sync-on-read change and you control the custom code
  • You have SSH or WP-CLI access and can switch PHP CLI to 8.2
  • You have a recent backup and a staging site to test on
  • You can read SQL and execute a backfill UPDATE confidently
  • You have not yet altered the HPOS table schema to force sync to finish

Hand it off, save the time

  • Your store has 50k+ orders with custom checkout flows or programmatic order creation in its history
  • You already dropped the UNIQUE constraint on wp_wc_orders to force sync to 100 percent
  • wp_wc_customer_lookup shows email or name mismatches against billing meta
  • The dashboard takes 10 seconds or more to load and Query Monitor shows 90k+ meta-table rows being scanned
  • You depend on a third-party plugin or SaaS integration that updates orders via wp_postmeta
  • The site is losing revenue every hour the order data is wrong

How to diagnose with WP-CLI

The Orders settings UI is too quiet to be useful for diagnosis. Run these in order from the command line and the failure mode reveals itself.

1
Confirm your PHP CLI version and your WooCommerce version

Run php -v and wp wc --version. If PHP CLI is 8.4 and WooCommerce is in the 10.4.x range, you are in the silent-rollback window. Set WP-CLI to use PHP 8.2 by editing your shebang, your wp-cli alias, or by invoking /usr/bin/php8.2 $(which wp) ... directly for the duration of the migration work.

2
Check the unmigrated count

Run wp wc hpos count_unmigrated. Note the number. If you re-run sync and this number does not decrease across multiple runs, sync is not actually writing data. Watching this counter is the cheapest way to confirm whether progress is real.

3
Verify the HPOS tables are receiving writes

Run SELECT COUNT(*) FROM wp_wc_orders; and SELECT COUNT(*) FROM wp_wc_orders_meta; in your database. If sync has been running for an hour and both return 0, you are in the silent-rollback or schema-violation scenario.

4
Run sync manually with small batches and watch for visible errors

Run wp wc hpos sync --batch-size=50. On PHP 8.2, errors surface immediately. The most common one is WordPress database error: Duplicate entry empty value for key wp_wc_orders.order_key, which points you to the next step. The terminal output is the real diagnostic, not the WooCommerce log file.

5
Audit your legacy data for empty _order_key

Run SELECT COUNT(*) FROM wp_postmeta WHERE meta_key = "_order_key" AND (meta_value = "" OR meta_value IS NULL); against your database. If the count is non-zero, those are the orders blocking sync. Also check for missing billing or shipping fields with SELECT COUNT(*) FROM wp_postmeta WHERE meta_key IN ("_billing_first_name","_billing_address_1") AND (meta_value = "" OR meta_value IS NULL);.

6
Check active-plugin HPOS compatibility

In the admin, go to WooCommerce then Settings then Advanced then Features and click the View and manage link near the HPOS section. Any plugin that has not declared HPOS compatibility is a candidate for breaking sync-on-read or for writing directly to wp_postmeta. From WP-CLI, wp wc hpos verify_compatibility returns the same data in a script-friendly format.

7
Check whether you are running WooCommerce 10.7 or later

If yes, sync-on-read is disabled by default. Any custom code writing to wp_postmeta is no longer reflected in HPOS reads. Search your codebase and active plugins for update_post_meta and wp_update_post calls scoped to orders. If you find any, that code is now silently broken and either needs the temporary filter or a CRUD-layer refactor.

How to fix it

Each fix branch below stands alone. Pick the one your diagnosis pointed to.

If sync runs silently and writes nothing (PHP 8.4 silent rollback)

Switch WP-CLI to PHP 8.2 for the migration. Reset any partial state, then re-run sync from the command line.

# Confirm CLI is now on PHP 8.2
wp --info

# Reset HPOS state if available in your version
wp wc hpos reset || echo "reset subcommand not available in this version"

# Run sync with small visible batches
wp wc hpos sync --batch-size=50

# Watch the unmigrated counter actually decrease
wp wc hpos count_unmigrated

If sync now fails with a visible error instead of running silently, that is the real underlying issue and you move to the next fix branch.

If sync crashes with Duplicate entry for wp_wc_orders.order_key

Backfill the missing _order_key values before retrying sync. Generate unique values for the affected orders.

-- Find orders with empty or missing _order_key
SELECT post_id
FROM wp_postmeta
WHERE meta_key = '_order_key'
  AND (meta_value = '' OR meta_value IS NULL);

-- Or find orders that never had the row at all
SELECT p.ID
FROM wp_posts p
LEFT JOIN wp_postmeta m
  ON p.ID = m.post_id AND m.meta_key = '_order_key'
WHERE p.post_type = 'shop_order'
  AND m.meta_id IS NULL;

-- Backfill with a unique value per order (WooCommerce uses 'wc_order_' prefix + random)
UPDATE wp_postmeta
SET meta_value = CONCAT('wc_order_', SUBSTRING(MD5(CONCAT(post_id, RAND())), 1, 13))
WHERE meta_key = '_order_key'
  AND (meta_value = '' OR meta_value IS NULL);

Also pre-validate billing and shipping fields. The HPOS wp_wc_order_addresses table has NOT NULL constraints. Any legacy order with empty billing name or address will block sync the same way.

Do not drop or modify the UNIQUE constraint on wp_wc_orders.order_key to force the sync to finish. That gets the percentage to 100 but the resulting HPOS state is unsupported, the Orders dashboard becomes very slow, and there is no built-in repair path.

If WooCommerce 10.7 broke your custom integrations (sync-on-read removal)

Temporary fix. Re-enable sync-on-read with the filter. Drop this in a must-use plugin or your theme functions file.

add_filter( 'woocommerce_hpos_enable_sync_on_read', '__return_true' );

This buys you time but does not solve the underlying issue. WooCommerce has signalled that the filter is a transition aid, not a long-term API.

Permanent fix. Refactor any custom code that writes orders through the post tables to use the CRUD layer.

// Wrong, no longer syncs to HPOS in WC 10.7+
update_post_meta( $order_id, '_billing_phone', '555-1234' );

// Right, writes to HPOS through CRUD
$order = wc_get_order( $order_id );
$order->set_billing_phone( '555-1234' );
$order->save();

For meta keys without a dedicated setter, use $order->update_meta_data("key", $value) followed by $order->save().

If you already forced sync to 100 percent and the Orders dashboard is broken

This is the most expensive path because there is no built-in repair. WooCommerce treats a schema with dropped constraints as unsupported. The supported sequence is:

  1. Take a full backup of the current database including the unsupported HPOS tables.
  2. Restore the HPOS tables to the official schema with UNIQUE and NOT NULL constraints intact. The schema definition lives in the WooCommerce source.
  3. Pre-validate every legacy order against the strict schema. Backfill missing _order_key, billing names, and shipping fields.
  4. Re-run wp wc hpos sync to completion against the clean schema. The clean run populates total_amount, order_key, and the indexed status and date columns correctly.
  5. Only then enable HPOS as the primary datastore. Performance should be evaluated only after this point.

If the legacy data cannot reasonably be made compliant in a reasonable time, archiving very old orders is a legitimate business decision, subject to your reporting, refund, and accounting needs.

Not sure which branch above applies, or do not want to run SQL backfills against your production database?

Let us fix it in 2 hours →

Data corruption most merchants miss

Even after sync reaches 100 percent and the dashboard loads fast, the data inside HPOS may not be what you think it is. Two specific corruptions hide on single-order screens and only surface in reports.

The wp_wc_customer_lookup mismatch

GitHub issue 60214, opened in August 2025 against the WooCommerce repository, documented that for a significant subset of registered customers, the email, first_name, and last_name in wp_wc_customer_lookup do not match the actual billing data on orders linked to the same customer_id. The reporter showed a concrete example: order 27854 with customer_id 774 had billing data for one customer, but the lookup table said customer 774 was a different person entirely. The root cause is a flaw in the initial HPOS migration that mapped legacy WordPress user IDs to the new HPOS customer IDs incorrectly. Detect it with a query like this:

-- Find customer_id values where the lookup email does not match the order billing email
SELECT
  o.id AS order_id,
  o.customer_id,
  c.email AS lookup_email,
  m.meta_value AS billing_email
FROM wp_wc_orders o
JOIN wp_wc_customer_lookup c
  ON o.customer_id = c.customer_id
LEFT JOIN wp_wc_orders_meta m
  ON o.id = m.order_id AND m.meta_key = '_billing_email'
WHERE c.email IS NOT NULL
  AND m.meta_value IS NOT NULL
  AND c.email != m.meta_value
LIMIT 100;

If this returns rows, your lookup table is corrupted. WooCommerce does not currently ship a one-click repair. The supported fix is a scripted UPDATE that rewrites the lookup table from the authoritative billing data, deduplicated by customer_id.

The empty total_amount on partially migrated orders

Orders that survived a sync run where constraints were bypassed often end up in wp_wc_orders with total_amount empty or zero. The individual order view still shows the correct total because it falls back to wp_postmeta. The Orders list view reads from wp_wc_orders directly and shows zero. Any analytics or accounting query that joins on total_amount understates revenue, which has real consequences. The only supported path is the clean-reschema sequence in the fix section above.

A real 276k-order debug session

Anonymized and condensed from a real WordPress.org support thread documenting a December 2025 migration debugging session, with permission of the pattern owner.

Scenario

A WooCommerce 10.4.0 store with 257,000 shop_order posts and 19,000 refunds on PHP 8.4.11 enabled HPOS compatibility mode. The Orders dashboard took 90 seconds to load. The sync progress bar said 276,750 orders pending and never moved over four days. Action Scheduler kept running batches and reporting them complete. No errors in the WooCommerce logs. No PHP errors. The hosting environment had 1 GB PHP memory and a tuned MySQL 8 instance, so it was not a resource problem.

Diagnosis

The merchant deactivated all plugins, switched to a default theme, and removed Query Monitor's db.php drop-in. Behavior unchanged. The WP-CLI command wp wc hpos count_unmigrated stayed at 276,750. SELECT COUNT(*) FROM wp_wc_orders returned zero. WooCommerce staff identified the cause as the PHP 8.4 silent transaction rollback issue. The merchant switched WP-CLI to PHP 8.2.29. Sync immediately failed with a visible error: Duplicate entry empty value for key wp_wc_orders.order_key. Roughly 250,000 of the legacy orders had been created through a custom checkout flow that never wrote billing names or _order_key into postmeta.

Resolution

The merchant pre-validated and backfilled the affected orders. Required address fields got placeholder values to satisfy NOT NULL. Empty _order_key rows got unique generated keys. The schema constraints stayed intact throughout. wp wc hpos backfill succeeded on a single test order. wp wc hpos sync --batch-size=50 then ran cleanly through the full 276,750 orders, populating total_amount, status, date_created_gmt, and order_key correctly. HPOS was then enabled as the primary datastore. The Orders dashboard dropped from 90 seconds to under one.

Total elapsed
Several days of debugging when done by the merchant from cold. We compress this into a 2-hour repair window because we have already done the diagnosis on dozens of stores.

When to stop and hand it off

Four scenarios where DIY costs more than help. First, you already dropped or modified the UNIQUE or NOT NULL constraints on wp_wc_orders to force the original sync to finish. The dashboard is slow and there is no official repair. The clean fix requires restoring the schema, re-validating every order, and re-running sync. Second, your store has 100k or more orders and any meaningful subset have custom or programmatic checkout history. The data-cleanup phase is the hard part and the wrong SQL costs you orders. Third, you depend on a paid third-party integration writing to wp_postmeta and you are now in WC 10.7+ and the vendor will not fix it. Fourth, the wp_wc_customer_lookup table is corrupted and your reporting is wrong, but you cannot afford a full re-migration. Our flat rate is $49 to $149, we finish in two hours when scope fits, and we refund in full if we cannot.

Get a quote in 60 seconds

WooCommerce HPOS FAQ

My HPOS sync runs all day but wp_wc_orders stays empty. Why?

You are almost certainly running PHP 8.4 with WooCommerce 10.4.x or older. WooCommerce confirmed in a December 2025 support thread that HPOS sync on PHP 8.4 produces silent transaction rollbacks. Action Scheduler reports batches complete, the progress percentage advances because batches counted are attempts not insertions, but no rows are committed to wp_wc_orders or wp_wc_orders_meta. WooCommerce logs and PHP error logs stay clean. The fastest verification is to switch WP-CLI to PHP 8.2 and run wp wc hpos sync directly. On 8.2 the underlying error becomes visible, almost always a Duplicate entry empty value for the UNIQUE order_key column, which you can then address with a backfill.

I see Duplicate entry empty for key wp_wc_orders.order_key during sync. What is happening?

Your store has legacy orders whose _order_key meta value is missing or an empty string. The wp_wc_orders table HPOS creates has a UNIQUE NOT NULL constraint on order_key, so the second attempted insert with an empty string crashes the entire batch. Stores that ran custom checkout flows, programmatic order creation, or very old historical orders frequently have this. The fix is to backfill _order_key for affected orders before running sync. Identify the affected orders with a SQL query against wp_postmeta filtered for empty _order_key values, generate new unique keys, then run wp wc hpos sync. Do not drop or modify the UNIQUE constraint on wp_wc_orders to get past this. WooCommerce treats that as an unsupported state and the Orders list view will fall back to expensive metadata scans afterward.

After enabling HPOS my Orders dashboard shows $0 totals for every migrated order. Why?

The total_amount column in wp_wc_orders did not populate during sync, which usually means the sync hit an error mid-record and either skipped writes or finished in an unsupported state. The individual Order screen still shows the right total because it falls back to reading from wp_postmeta. The list view reads total_amount directly from wp_wc_orders, which is empty. You cannot repair this in place. The supported fix is to restore the HPOS table schema to its official definition, pre-validate the legacy orders so every required column has data, then re-run wp wc hpos sync to completion against a clean schema. There is no built-in WP-CLI command to partially repair a corrupted HPOS table once constraints have been bypassed.

WooCommerce 10.7 shipped in April 2026 and now my custom code that updates orders does not reflect in the admin. What changed?

WooCommerce 10.7 disabled sync on read for HPOS by default. Before 10.7, if your custom code or a non-HPOS-aware plugin wrote order data directly to wp_posts or wp_postmeta with wp_update_post or update_post_meta, WooCommerce would lazily sync that change back into HPOS the next time the order was read. After 10.7, that fallback is off and your changes stay invisible to the HPOS-backed Orders list. The temporary fix is the woocommerce_hpos_enable_sync_on_read filter that returns true. The permanent fix is to refactor your custom code to use the WooCommerce CRUD layer, specifically wc_get_order and the OrderUtil methods, instead of writing to the post tables directly. The admin notice WooCommerce showed after the upgrade was dismissible, so most affected merchants missed it.

I enabled compatibility mode hoping the slow Orders dashboard would speed up. It is still slow. Why?

Compatibility mode does not improve admin performance. WooCommerce confirmed this directly in support. With HPOS in compatibility mode and not set as the primary datastore, WooCommerce continues to read orders from wp_posts and wp_postmeta. On a 250k order store that means the same slow legacy queries you started with. Compatibility mode only keeps the two storage systems in sync so you can roll back if HPOS itself causes issues. The performance gain only arrives when you switch the primary datastore to HPOS, which requires running the sync to completion cleanly first. Enabling compatibility mode without then completing migration is a common dead end.

Is the wp_wc_customer_lookup table corrupted on my store and how would I know?

A bug opened in August 2025 against the HPOS migrator (GitHub issue 60214) confirmed that for a significant subset of registered customers, the email, first_name, and last_name in wp_wc_customer_lookup do not match the actual billing data on orders linked to the same customer_id. The corruption hides on single order screens because the order page reads billing data from order meta, which is correct. It only surfaces in aggregate reports, analytics, or any custom query that joins wp_wc_customer_lookup against wp_wc_orders by customer_id. To detect it, run a SELECT that joins the two tables on customer_id and compares the lookup email or name against the billing meta. Mismatches indicate the migrator mapped legacy user_id to customer_id incorrectly. There is no official one-click repair. The fix is a scripted UPDATE that rewrites the lookup table from the correct billing source.

How do I safely roll back from HPOS to the legacy posts-based storage if migration broke my store?

Only possible if compatibility mode was enabled before you switched primary storage to HPOS. In that case, go to WooCommerce then Settings then Advanced then Features, find the order storage section, and switch back to WordPress posts storage. WooCommerce then reads from wp_posts and wp_postmeta again. Disable compatibility mode after, otherwise sync runs forever for nothing. If you did not have compatibility mode on when you flipped to HPOS, any orders created after the flip exist only in wp_wc_orders and reverting will hide them. You then need to either run a reverse-sync script or accept that those orders are now lost from the legacy view. We do these rollbacks as flat-rate fixes when the data state allows it.

Sources and further reading

Every WooCommerce-specific claim on this page traces back to WooCommerce's own documentation, the WooCommerce GitHub repository, or the WordPress.org support forum.

Why we fix WooCommerce HPOS migrations faster than WooCommerce support tickets

2h

2-Hour Guarantee

Fixed in 2 hours or your money back. Support threads on HPOS routinely run weeks.

$49

Flat Rate $49 to $149

No hourly billing. We have done HPOS recoveries on stores from a few hundred to several hundred thousand orders.

100%

Money-Back Guarantee

Cannot fix it? You do not pay. Zero risk to you.

Stop fighting your HPOS migration

We have shipped this fix on WooCommerce stores every week since the WC 10.7 release. Flat $49 to $149, done in 2 hours when scope fits, money back if we cannot.

Fix My WooCommerce Store Now