WooCommerce HPOS Migration Errors and How to Fix Them
If you turned on WooCommerce's High-Performance Order Storage and orders suddenly look wrong — missing from the admin list, stuck mid-sync, or duplicated between old and new tables — you're not alone. HPOS moves order data out of wp_posts into dedicated tables, and on real stores with a few plugins and a few years of history, that move rarely goes perfectly on the first try. Here's how to read the errors, find the actual cause, and get the sync finished cleanly.
What HPOS actually changes
Up through WooCommerce 7.x, every order was stored as a custom post type row in wp_posts, with order meta scattered across wp_postmeta. That works, but it's slow at scale — postmeta is a generic key-value table, and a store with tens of thousands of orders ends up with a bloated, hard-to-index meta table.
High-Performance Order Storage (HPOS), also called Custom Order Tables, gives orders their own purpose-built schema:
wp_wc_orders— the order itself (status, totals, customer, dates)wp_wc_order_operational_data— shipping, tax, and payment detailswp_wc_orders_meta— anything custom, still key-value but scoped to orders onlywp_wc_order_addresses— billing and shipping addresses
WooCommerce has been nudging every store toward this since 8.2, and newer versions sync by default. The problem isn't HPOS itself — it's the transition, especially on stores where a theme, a plugin, or a custom snippet still queries orders the old way.
How to check which mode you're actually in
Don't assume. Go to WooCommerce → Settings → Advanced → Features, or check it from the command line if you have SSH/WP-CLI access on your VPS or via cPanel Terminal:
wp option get woocommerce_custom_orders_table_enabled
wp option get woocommerce_custom_orders_table_data_sync_enabled
If the first returns no but the second is yes, you're mid-migration — both storage systems are being kept in sync, and that's usually when things get slow or stuck.
Symptom: orders are missing or duplicated after enabling HPOS
Cause: almost always a plugin, custom theme code, or an old report/export tool querying wp_posts directly with post_type = 'shop_order' instead of going through WooCommerce's CRUD functions (wc_get_orders(), wc_get_order()). Once HPOS is the source of truth, that direct query returns nothing, so the plugin "thinks" the order doesn't exist and may recreate it, or simply drops it from its own list.
Fix: before you enable HPOS on a live store, check compatibility first. WooCommerce flags it for you:
- Go to WooCommerce → Settings → Advanced → Features and look at the "Plugin compatibility" panel — it lists every active plugin as compatible, incompatible, or unknown.
- For anything marked "incompatible," check the plugin's changelog for an HPOS-compatible release before touching production. Common offenders: older shipping label plugins, custom invoicing/PDF generators, and older accounting/export integrations.
- If a plugin has no HPOS support and no fix in sight, keep compatibility mode ("data sync") on indefinitely rather than forcing a full cutover — it's slower, but it keeps both tables current so nothing silently breaks.
Symptom: sync never finishes ("X orders remaining" stays stuck)
Cause: the background sync runs through WP-Cron, in small batches, on top of normal site traffic. On a shared cPanel account or a small VPS, this competes with real requests for PHP-FPM workers and often gets throttled, times out, or silently stops firing if WP-Cron isn't running reliably.
Fix: run the sync manually instead of waiting on cron, in batches you control:
wp wc hpos sync --batch-size=200
wp wc hpos status
If wp isn't available (common on shared cPanel hosting without SSH), do it from the dashboard: WooCommerce → Settings → Advanced → Features → Synchronize now. Watch it in a browser tab and don't navigate away — on hosting with a short PHP max_execution_time, a large batch can get cut off mid-run. If your account has a PHP execution timeout under 60 seconds, raise it temporarily through cPanel's MultiPHP INI Editor for the duration of the sync, then set it back.
For very large order histories (50,000+), don't run it in one pass. Sync in chunks during low-traffic hours, and confirm the "orders remaining" count is actually dropping between runs — if it's stuck at the same number after two batches, something is failing silently. Check wp-content/debug.log or your PHP error log in cPanel for entries mentioning wc_get_orders or DataSynchronizer.
Symptom: database errors during the table creation step
Cause: HPOS creates four new tables the first time you enable it. If your database user doesn't have CREATE, ALTER, or INDEX privileges — which happens on some restricted or reseller-managed cPanel database users — that step fails partway, leaving you with some tables created and others missing.
Fix: in cPanel, go to MySQL® Databases, scroll to "Add User to Database," and confirm the WordPress database user has All Privileges checked, or at minimum CREATE, ALTER, INDEX, SELECT, INSERT, UPDATE, and DELETE. Then check which HPOS tables actually exist via phpMyAdmin:
SHOW TABLES LIKE 'wp_wc_order%';
If some are missing, disable HPOS, fix privileges, and re-enable — WooCommerce will recreate whatever's absent rather than erroring on tables that already exist.
Legacy storage vs. HPOS at a glance
| Aspect | Legacy (wp_posts) | HPOS (custom tables) |
|---|---|---|
| Order data location | wp_posts + wp_postmeta | wp_wc_orders + related tables |
| Query performance at scale | Degrades as postmeta grows | Stays flat, purpose-built indexes |
| Plugin compatibility | Universal — every plugin expects this | Needs explicit plugin support |
| Migration required | None (default) | One-time sync, ideally in compatibility mode first |
| Best for | Small catalogs, heavy legacy plugin use | High order volume, modern plugin stack |
Prevention: how to migrate without a bad day
- Take a full database backup before enabling HPOS — from cPanel's Backup tool or
mysqldumpif you have shell access. This step is reversible, but only if you've got a snapshot to fall back to. - Test the migration on a staging copy first. Most cPanel accounts include a one-click staging tool; use it. Enable HPOS there, watch for plugin errors, and only replicate the change to production once it's clean.
- Leave "data sync" (compatibility mode) enabled for at least a week after cutover. It costs a bit of database overhead but means legacy-mode plugins keep working while you find and fix the rest.
- Re-check plugin compatibility after every major WooCommerce or plugin update — a plugin that was fine last month can regress with a new release.
- If you're on shared cPanel hosting and the sync keeps timing out, that's usually a sign the store has outgrown shared resources for order volume — a VPS with dedicated PHP-FPM workers handles large syncs far more reliably.
When to just roll it back
If you've enabled HPOS on a live store and orders are actively disappearing from customer views or the admin, don't troubleshoot live. Go to WooCommerce → Settings → Advanced → Features and switch back to "Legacy storage" — this doesn't delete the new tables, it just stops using them as the source of truth, so you can fix compatibility issues without customers seeing broken orders in the meantime.
Frequently asked questions
Do I have to migrate to HPOS, or can I stay on legacy storage?
You can stay on legacy storage for now — WooCommerce still supports it. But newer WooCommerce versions increasingly assume HPOS, and staying on legacy long-term means missing performance improvements and, eventually, plugin support. If your store is small and stable, there's no rush; if you're doing high order volume, migrate on staging first.
Will enabling HPOS delete my old order data?
No. Enabling HPOS copies order data into the new tables; it doesn't remove anything from wp_posts unless you explicitly run WooCommerce's cleanup tool after confirming the sync is complete and stable. Keep both until you're fully confident.
How long does the HPOS sync take?
It depends on order count and server resources. A few hundred orders can finish in minutes; tens of thousands can take hours if run in small background batches. Running it manually in larger batches during low-traffic hours is usually faster than waiting on WP-Cron.
My database user can't create new tables on cPanel — is that normal?
Some reseller or restricted cPanel database users only get SELECT/INSERT/UPDATE/DELETE by default. Check the user's privileges under MySQL® Databases in cPanel and add CREATE, ALTER, and INDEX if they're missing — HPOS needs them to build its tables the first time.
Can I roll back to legacy storage after switching to HPOS?
Yes, as long as you didn't run the cleanup step that deletes the old post-based order data. Go to WooCommerce > Settings > Advanced > Features and switch the authoritative storage back to legacy — it's a toggle, not a destructive migration.