Why Your WooCommerce Order Export Misses Data
September 27, 2026
Your export plugin ran without a single error. Your order count still doesn't match. This is the moment most people conclude the plugin is broken and go looking for a replacement, when the real problem is almost never the plugin. It's the data underneath it.
WooCommerce order exports fail silently more often than they fail loudly. No red banner, no stack trace, just a CSV that quietly has fewer rows than your dashboard, or rows with blank columns where custom fields should be. Across the multi-channel stores I've worked on that sync orders into accounting systems, I've traced this exact complaint back to one of a handful of causes, and it's rarely the export tool itself. It's the architecture the export tool is querying against.
The postmeta problem: why custom fields vanish from exports
WooCommerce stores order data in two layers. The basics, like order ID, date, and status, live in the core wp_posts table, because an order is really just a custom post type under the hood. Everything else, payment gateway details, fulfillment notes, custom statuses, any field you or a plugin added later, gets stored as postmeta in wp_postmeta. According to WooCommerce's developer documentation, WooCommerce orders extend the WordPress post type system, and extended fields are stored in postmeta.
If your export plugin only queries wp_posts, it gets order IDs and dates and stops there. It never touches the layer where most of the useful order context actually lives.
It gets worse with the REST API. Per the WordPress Plugin Developer Handbook, custom post meta is not exposed in the REST API unless it's explicitly registered with register_post_meta or register_rest_field. That's not an oversight, it's the default security posture. WordPress won't hand arbitrary postmeta to any API caller unless someone told it to.
The practical fallout: many export plugins register only the fields they were coded for. Your custom order field, the one you added for your own workflow, simply doesn't exist as far as the API is concerned. It's not hidden, it's not filtered, it's invisible at the query level. Switching to a different export plugin won't fix this unless that plugin happens to register the exact same field, which is unlikely if it's specific to your business.
The fix starts with tracing which fields are missing, then checking whether they're registered for REST at all. If they're not, registration is the actual fix, not a new plugin.
Serialized data and MySQL packet limits: when order metadata won't insert or retrieve
Postmeta values get serialized before storage. That means a single postmeta row can hold an entire PHP array crammed into one text field, payment details, shipping calculation logs, custom line-item data, all serialized together.
Serialized data has a size ceiling, and that ceiling is your MySQL server's max_allowed_packet setting. According to MySQL's own documentation, packets larger than max_allowed_packet are rejected by the server and result in a packet error. On shared hosting this is often set around 4MB; on managed WordPress hosts it might be 16MB. Either way, if a serialized postmeta value crosses that line, the database rejects the insert, or fails silently on retrieval.
Here's the part that trips people up: when an export query hits this limit, the database doesn't throw a visible error up to the application. It returns an incomplete result set. The export tool has no idea anything went wrong. It proceeds with partial data, and you find out only when you notice a field is missing weeks later.
You can check your own limit by running SELECT @@max_allowed_packet; against your database. If that number is below 8MB and you're storing large serialized arrays in order meta, that's likely your culprit.
If this is what's happening on your site, no plugin swap fixes it. You either ask your host to raise the packet limit, or you refactor what you're storing so you're not cramming huge serialized arrays into a single postmeta row in the first place.
Line items live in a separate schema: exports that skip the order_items table
This is the one that catches people who've already checked postmeta and still can't find their data. Order line items, SKUs, quantities, per-item costs, variation data, are not stored in wp_postmeta at all. They live in two dedicated tables: wp_woocommerce_order_items and wp_woocommerce_order_itemmeta. WooCommerce's documentation is explicit that order items and item metadata sit in tables separate from order postmeta.
If your export plugin or script only queries orders and postmeta, any custom field attached at the line-item level, something like a supplier SKU or a drop-ship location, is invisible to it. Not filtered out, not empty by mistake. The query never looked at the table where that data lives.
A correct export needs to join the order_items tables, iterate through each item on the order, and pull the item-level metadata separately. Most off-the-shelf CSV export plugins don't do this by default, and some don't do it at all unless you configure a specific add-on for line-item fields.
If your export shows accurate order totals but blank product-level custom fields, that's the tell. The plugin isn't reading order_items. A different plugin with the same limitation gets you the same blank columns. This is exactly the kind of gap I've seen push people toward building a custom WooCommerce plugin instead of installing another one, because once you need a join across order tables that no export UI exposes, you're better off writing the query yourself.
Status filters and timezone mismatches: why whole orders disappear from the export
Sometimes it's not a field that's missing, it's entire orders. This usually comes down to status filters.
Export plugins commonly default to a shortlist of statuses: Processing, Completed, maybe Refunded. Pending, draft, and any custom status you've created for your workflow get silently excluded unless you explicitly add them. If your store uses a custom status like "Awaiting Approval," WooCommerce's own documentation notes that status filters are applied during export queries and non-standard statuses are excluded unless requested. You can end up with a CSV that's missing a meaningful chunk of your orders, and the export still completes without a single error.
Timezone mismatches compound the problem in a different way. If your WordPress site timezone is set to UTC but the server's PHP timezone is set to something else, a date-range filter like "export today's orders" can skip or include the wrong set of orders entirely. According to WordPress.org's own support documentation, incorrect timezone configuration causes date queries to return unexpected result sets. You think you exported January 1st and you actually got December 31st.
Two checks fix most of this. First, look at your export tool's status filter settings and explicitly include every status you actually use, not just the defaults. Second, verify WP_TIMEZONE_STRING in wp-config.php matches your actual business timezone, and that it agrees with your server's PHP timezone setting. If you're changing timezone or wp-config values, back the file up and test on a staging copy first, since a mismatch here can quietly shift more than just export results.
None of this is a data corruption problem. It's a filter problem. But to the person staring at a CSV, it looks identical: orders are missing, and nothing threw an error.
WP-Cron and loopback failures: when scheduled exports silently skip
If you've automated your export to run on a schedule, you're relying on WP-Cron, and WP-Cron is not a real cron job. According to the WordPress Plugin Developer Handbook, WP-Cron is triggered by site visits, not by a system clock, and it can fail silently without a real system cron in place.
On a low-traffic site, or behind a server configuration that blocks loopback requests, WP-Cron tasks simply don't fire. Your scheduled export doesn't run, and there's no error dialog telling you that. Nobody notices until someone checks the accounting system and realizes orders haven't landed in days.
Even when WP-Cron does fire, a database error or a timeout mid-export can cause the task to fail without logging anything. The export starts, hits a lock or a timeout, and exits without ever writing a completion flag. From the outside it looks like nothing happened, because functionally, nothing did.
I've dealt with this exact failure mode on scheduled data syncs before, and the fix is the same one I'd give for any critical automated task: don't trust WP-Cron alone. Set up a real system cron job to hit a webhook or admin endpoint that triggers the export directly. I've written more on this pattern in why your scheduled API sync keeps missing data, which covers the same silent-failure problem from the sync side.
A quick sanity check: visit your site's own URL from the server itself and see if the loopback request succeeds. If it fails, WP-Cron is broken on that server, and any export you think is running on a schedule almost certainly isn't.
Database permissions and silent query failures: the shared hosting gotcha
The last layer, and the one people check last even though it's often the easiest to verify, is database permissions.
On shared hosting, database users are frequently restricted: SELECT-only access on certain tables, no access to system tables, limited function permissions. According to MySQL's own privilege documentation, insufficient SELECT permissions on a table result in incomplete query results, and critically, without explicit error reporting.
If your export script's database user doesn't have SELECT permission on wp_woocommerce_order_items or wp_woocommerce_order_itemmeta, that part of the query returns an empty result. No exception, no warning. The export completes, and it's just missing everything that depended on that table.
Table locks during high-traffic periods create a similar effect. A query times out, returns whatever it managed to gather, and moves on. The export finishes, technically successfully, with a fraction of the data it should have had.
Before you swap plugins over this, verify permissions directly. Log into phpMyAdmin, or ask your host, and run a plain SELECT against every table your export touches: wp_posts, wp_postmeta, wp_woocommerce_order_items, wp_woocommerce_order_itemmeta. If one of those comes back restricted or empty when it shouldn't be, that's your answer. You either get the permission raised with your host, or you redesign the export to work within what your database user can actually read. No plugin, however well reviewed, can export data from a table it isn't allowed to see.
How to actually diagnose this instead of guessing
The pattern across all six of these causes is the same: the export completes, and the failure is invisible unless you go looking for it at the right layer. So go looking, methodically, rather than reinstalling a different plugin and hoping.
Start with the cheapest check: compare your WooCommerce order count in the dashboard to the row count in your export file. A quick SELECT COUNT(*) FROM wp_posts WHERE post_type='shop_order'; against your database, matched to your CSV row count, tells you immediately whether this is a missing-orders problem or a missing-fields problem. If the counts match but fields are blank, you're looking at postmeta registration or an order_items join issue. If the counts don't match, check status filters and timezone settings first, since those are the most common reasons whole orders vanish.
From there:
- If specific custom fields are blank across all orders, check whether they're registered with
register_post_meta. If they're not, no plugin will ever show them, registration comes first. - If line-item level fields are blank but order-level fields are fine, your export isn't joining
wp_woocommerce_order_itemsandwp_woocommerce_order_itemmeta. That's a query problem, not a plugin problem. - If order counts are off and it correlates with certain statuses or a date boundary, you're dealing with filters or timezone, not data loss at all.
Across the marketplace and multi-channel projects I've built and maintained, this is consistently where the real time goes: not choosing between two export plugins, but figuring out which layer of WooCommerce's schema the current tool never reaches. Once you know that, the fix is usually narrow, register a field, add a join, fix a timezone setting, raise a packet limit, rather than a wholesale rebuild.
If your schema is non-standard, custom line-item fields, serialized data, unusual statuses, a plugin built for the general case is going to keep failing in the same specific way no matter how many times you replace it. At that point a short custom export script using direct SQL queries against the tables you actually need gives you full visibility into what's being pulled, and it stops the guessing entirely. If you're stitching WooCommerce into an accounting system or a warehouse tool and want the integration itself to be solid rather than just the export, that's the kind of work I do under API and webhook integration, and it pairs with a lot of what I've written about [
FAQ
How do I know if my export is missing data?
Compare your WooCommerce order count in the dashboard to the number of rows in your export file. If they don't match, filter by status, your plugin may be excluding pending or draft orders. Then check a few orders manually: do exported custom fields match what's in the order details? Missing line-item fields suggest the export isn't joining the order_items tables.
Can I fix missing custom fields by switching export plugins?
Not if the fields aren't registered with register_post_meta. The API won't expose them to any plugin. Register your custom fields for REST first, then re-export. If they still don't appear, the plugin isn't querying wp_postmeta or the fields are stored in order_itemmeta, not postmeta.
What's the fastest way to diagnose a missing-data export?
Write a quick SQL query: SELECT COUNT(*) FROM wp_posts WHERE post_type='shop_order'; then count rows in your CSV. If counts match, the issue is field-level (postmeta, line items). If counts don't match, check status filters and timezone settings. Query wp_postmeta directly to verify custom fields exist in the database.
Should I build a custom export script or use a plugin?
If your schema is non-standard, custom line-item fields, serialized data, unique status filters, a plugin won't work without modification anyway. A custom script using direct SQL queries gives you full control and visibility into what's being queried. Plugins are fine for standard WooCommerce exports; complexity demands custom code.