WooCommerce 11 defers database deletion of removed order items until the next order save. The pre-hook still fires synchronously, but the `woocommerce_removed_order_items` post-hook moves to `save_items()` after the delete commits. Extensions that bracket one operation across both hooks can break.
Use this for payment, fulfillment, tax, subscription, ERP, audit, reporting, and custom-order extensions that listen to order-item removal or inspect persisted item rows during checkout resume and order edits.
Quick answer
Search the extension fleet for both removal actions, classify what each callback assumes, then test remove without save, remove followed by save, exception before save, double checkout, repeated save, and concurrent order updates. Logic that needs persisted deletion must run after save, while observation-only callbacks can remain on the post-hook.
What to check first
- Search custom plugins, themes, mu-plugins, vendor packages, snippets, and integration code for `woocommerce_remove_order_items`, `woocommerce_removed_order_items`, `remove_order_items()`, and assumptions about immediate database deletion.
- Record callback priority, accepted arguments, order type, data store, HPOS mode, transaction boundary, expected item rows, external writes, and whether the callback expects both hooks on one call stack.
- Test remove without save, remove then save, exception before save, failed order resume, gateway-triggered double checkout, repeated save, manual order edit, refund item removal, and concurrent update.
- At each point compare in-memory items, persisted order-item rows, totals, notes, metadata, hook timestamps, callback output, external records, and error logs.
- Move persisted-state logic after save, add durable idempotency where callbacks write externally, and keep the pre-hook only for work that truly belongs before in-memory removal.
Diagnostic table
Use this table to keep the work practical. It connects the symptom to evidence and a verification step.
| Action | Evidence to collect | How to verify |
| Inventory removal callbacks | Search custom plugins, themes, mu-plugins, vendor packages, snippets, and integration code for `woocommerce_remove_order_items`, `woocommerce_removed_order_items`, `remove_order_items()`, and assumptions about immediate database deletion. | Removing items without save leaves the original database rows intact. |
| Document timing assumptions | Record callback priority, accepted arguments, order type, data store, HPOS mode, transaction boundary, expected item rows, external writes, and whether the callback expects both hooks on one call stack. | Saving deletes the intended rows and then fires the post-hook with the expected final order state. |
| Build interrupted-save fixtures | Test remove without save, remove then save, exception before save, failed order resume, gateway-triggered double checkout, repeated save, manual order edit, refund item removal, and concurrent update. | An exception before save leaves the order recoverable with line items and totals intact. |
| Move persisted logic after save | At each point compare in-memory items, persisted order-item rows, totals, notes, metadata, hook timestamps, callback output, external records, and error logs. | Repeated save, resume, and callback execution do not duplicate audit, ERP, tax, or fulfillment writes. |
Why this usually happens
- WooCommerce changed deletion timing to prevent persisted line items from disappearing when resume fails before save.
- Extensions may have treated the pre and post hooks as one synchronous bracket.
- A callback can inspect the database after remove returns and still see the old rows until save.
- Retry or repeated save can duplicate an external effect when callback identity is not durable.
Useful command or data shape
Adapt paths, IDs, and privacy handling to the site before running commands or storing data on production.
add_action( 'woocommerce_removed_order_items', function( $order_id ) {
$order = wc_get_order( $order_id );
// Observe the persisted state after save. Keep external writes idempotent.
}, 10, 1 );
# Search the codebase before testing.
rg 'woocommerce_(remove|removed)_order_items|remove_order_items' wp-content
Safe fix order
Do the work in a sequence that makes each result easy to prove. Stop if a step produces new evidence that changes the incident scope.
- Inventory removal callbacks
- Document timing assumptions
- Build interrupted-save fixtures
- Move persisted logic after save
- Retest retries and external writes
Decision rule
Pass when interrupted flows retain persisted items, the post-hook sees committed deletion after save, extension callbacks run at the intended boundary, and repeated or concurrent saves create no duplicate external effect.
What to tell the client or owner
Give the owner the affected versions, exact workflow, observed result, business impact, evidence location, temporary control, named owner, and next review time. Remove credentials and personal data from shared screenshots and logs.
Production verification checklist
- Removing items without save leaves the original database rows intact.
- Saving deletes the intended rows and then fires the post-hook with the expected final order state.
- An exception before save leaves the order recoverable with line items and totals intact.
- Repeated save, resume, and callback execution do not duplicate audit, ERP, tax, or fulfillment writes.
Mistakes to avoid
- Do not change several plugins, cache rules, or infrastructure settings before preserving a baseline.
- Do not treat one successful test as proof for retries, alternate clients, background work, or mixed-version fleets.
- Do not paste secrets, personal data, complete production payloads, or customer files into tickets or screenshots.
- Do not close the test until the final user-visible state and the server-side evidence agree.
Questions teams ask during testing
Did the pre-hook change?
No. The advisory says `woocommerce_remove_order_items` still fires synchronously at the start of removal.
Does every listener need code changes?
No. A callback that only observes the final persisted state can continue using the post-hook, but it still needs regression coverage.
When HandL WP should help
Bring in HandL WP when a production checkout, form, editor, security gate, media pipeline, or paid lead workflow is at risk. We can preserve evidence, isolate the failing layer, make the smallest corrective change, and verify the result across WordPress, connected services, logs, and the user journey.
If this is active on a production site, test a WooCommerce 11 extension migration.
Related HandL WP guides
Use these related guides when the same issue touches tracking, security, checkout, or crawler visibility.
Migrate merchant-facing reserved item meta
Use the WooCommerce reserved order item meta migration audit to classify hidden keys, protect sensitive values, add supported display paths, and verify HPOS, email, REST, export, and fulfillment readers.
Test the 11.1 ID-scoped deletion method
Custom order data stores should continue with the WooCommerce 11.1 delete_items_by_ids compatibility test. It proves original item IDs are removed once, replacement items survive, and hooks, HPOS, retries, and downstream systems reconcile.
Helpful references