WooCommerce Subscriptions 9.2.0 runs its upgrade on the next request from an administrator, not on the update
The developer note for Subscriptions 9.2.0 warns that a function now returns false for cron, WP-CLI and unauthenticated REST calls. Read against the code, that function decides whether a link is shown on My Account, and nothing that removes items from a subscription goes through it - your automations are not affected. Four other changes in the same release are, and the routine that migrates a store onto them waits for a request made as an administrator - a login, or an API call on an administrator's key - so a shop updated by auto-update or from the command line runs the new code on the old data until one arrives.
A release with a warning for automations in it
WooCommerce Subscriptions 9.2.0 came out on 8 September, thirty-four days after 9.1.0, which was a security release - its advisory said to "update to version 9.1.0 or later immediately" and described "several security vulnerabilities, the most serious of which an unauthorized user could assume site control". 9.2.0 is the tidying-up that follows a release like that, and its developer note lists the changes "worth knowing if you build extensions, gateways, or custom integrations for Subscriptions". One paragraph in it reads like a warning for anyone who touches subscriptions from outside the site:
Two things to know if you call or filter it. The capability check runs after the existing wcs_can_items_be_removed filter and is combined with it, so a filter can still narrow the result but can no longer grant removal on its own; the filter now receives the user as a third argument. With no user to check, the function returns false, so calls from cron, WP-CLI, or an unauthenticated REST request now return false where they previously could return true.
- WooCommerce Subscriptions 9.2.0: settings updates and dev changes, WooCommerce Developer Blog, 8 September 2026, read 12 September 2026
Cron, WP-CLI, REST: that is the list of ways an n8n workflow, a Make scenario or a scheduled script reaches a subscription. If the paragraph meant what it appears to mean, a job that removed a line item after the third renewal would have started silently leaving the item in place on 8 September. It does not mean that, and the reason is in the sentence before it, which the paragraph is a footnote to: "The visible effect is small - the recipient of a gifted subscription no longer sees the remove-item control on My Account."
What the function actually decides
wcs_can_items_be_removed() answers one question: should the Remove link be
printed next to the items on a subscription's My Account page. In the 9.2.0
source it is called from exactly two places, templates/myaccount/subscription-totals.php
and templates/gifting/subscription-totals.php - both templates, both My
Account. The plugin's own changelog line describes it the same way: the filter
"can no longer be used on its own to show the remove item control". Nothing
that performs a removal calls it. The REST controller for wc/v3/subscriptions,
which is where a workflow sends a PUT with a line item at quantity zero, calls
$subscription->remove_item() directly and did not change between 9.1.0 and
9.2.0 - the file is byte-identical. The plugin's WP-CLI commands are two, for
generating and purging test data, and neither touches line items. Cron in the
plugin does not call the function either.
So the effect on an integration that removes items is nil, and the platforms confirm it from their side: n8n's WooCommerce node handles customers, orders and products and has no subscription resource, so subscriptions from n8n are an HTTP Request node against the REST endpoint above; Make's WooCommerce app is the same set of modules plus "Make an API call"; Zapier's subscription actions run through the paid WooCommerce Zapier extension, whose documentation says "Removed Line Items" on an update are "always ignored".
The paragraph is true of one thing: custom PHP on the site that calls the
function itself before removing an item. What the note does not say is who else
gets false from it now. The capability it checks,
edit_shop_subscription_line_items, is granted in one place in the plugin,
unchanged since 9.1.0:
if ( $subscription && $user_id === $subscription->get_user_id() ) {
$allcaps['edit_shop_subscription_line_items'] = true;
}Only the subscription's owner holds it. An administrator, a shop manager, the
user behind a REST key - all false, unless they happen to be the customer.
The one case that passes with no user at all is a guest subscription, where a
customer ID of zero matches a user ID of zero; the function's own docblock
points this out. If you have code that used this function as a permission
gate for staff, the documented way forward is the user_has_cap filter, not
the function's own filter, which "can still narrow the result but cannot widen
it past the capability".
The same section says the removal and resubscribe links "now mint a nonce for their own operation" and that "links generated before 9.2.0 no longer verify". Those links are printed on My Account and on product pages, not in emails - the plugin's email templates contain neither - and a WordPress nonce is valid for between twelve and twenty-four hours and tied to the logged-in user, so nothing generated before the update was going to be alive long after it. The one thing lost is the "Undo?" offered right after a removal: the changelog says "an item removed shortly before updating stays removed unless the store restores it".
None of that is what a store owner needs to act on. Four other things in the release are.
The upgrade routine waits for an administrator's request
9.2.0 ships an upgrade routine with four steps: normalise two manual-renewal
settings that could contradict each other, keep the old switch-button label,
create a downloads table, and schedule the gifting migration described below.
The upgrader that runs them is gated, in
includes/core/upgrades/class-wc-subscriptions-upgrader.php at lines 98-100
of the 9.2.0 source:
if ( @current_user_can( 'activate_plugins' ) && ( self::$plugin_upgrades_may_be_needed || self::$core_library_upgrades_may_be_needed ) ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors
// Run upgrades as soon as admin hits site
add_action( 'wp_loaded', [ __CLASS__, 'upgrade' ], 11 );A request from someone who can activate plugins. Not a cron tick, not the
WP-CLI process that ran wp plugin update without a --user, not the hosting
panel's auto-update. A request is not only a browser: WooCommerce authenticates
REST calls by consumer key before this check runs, so a GET or PUT on
wc/v3/subscriptions signed with a key that was created under an
administrator passes it too - which means a store that an n8n or Make workflow
polls on an admin-level key runs the routine on the workflow's next call. The
plugin's authors know the gate is there; the docblock on the manual-renewal
step says so:
This runs from the standard capability-gated upgrader, so a store updated without an admin request keeps the pre-normalization behavior gap until an admin next visits wp-admin - an accepted trade-off.
class-wcs-plugin-upgrade-9-2-0.php, WooCommerce Subscriptions 9.2.0, read 12 September 2026 from a public mirror of the release
For a store that updates plugins by hand, this is invisible: the administrator who clicked Update is the administrator whose next page load runs the routine. For a store on WordPress auto-updates, on a host that applies plugin updates overnight, or maintained over WP-CLI by an agency that rarely opens wp-admin, and whose integrations use a shop-manager key or none, the plugin is on 9.2.0 and its stored settings are on 9.1.0 until the next administrator's request. In that window the new code reads the old options. For gifting, below, that is the old behaviour. For the manual-renewal pair it is not: the new rule is already in force against the unnormalised settings, and the plugin's own docblock says the new rule "would silently flip such a store to automatic renewals - charging saved payment methods the merchant explicitly turned off" until the routine corrects it. The migration happens on that request, in the background, on whatever afternoon that turns out to be rather than on the night of the update.
Gifting is no longer a store-wide setting
The larger of the two migrated changes:
While Enable gifting for subscriptions is still available - Enabled/Disabled for all products default has been removed. Gifting is now set per product via the _subscription_gifting meta key, and a new bulk-edit option turns it on or off across many products at once.
If the storewide default was "enabled for all products," a background migration writes that value onto existing products that don't already have one. Stores that had gifting off need no migration since off is the new default for products with no value set.
The migration has no filter, so the per-product meta and the bulk edit are the only ways to adjust gifting on existing products.
- same developer note
The migration runs through Action Scheduler in batches of fifty, and it covers products that existed at the moment the routine started - the code records the highest post ID at that point and stops there, on purpose, so that a product created during the migration is not swept up by a rule that no longer applies to new products. The ceiling is the part a store with "enabled for all products" will discover later: every product created after the routine ran starts with gifting off. There is no setting to change that back. A store that sells gift subscriptions and adds a product a month will find, on the day a customer asks, that the newest ones cannot be gifted, and nothing in the product editor's defaults will have told the person who created them.
The smaller migrated change is the manual-renewal pair. "Turn off automatic payments" used to force manual renewal even when "Accept Manual Renewals" was off; now it requires both. The code's docblock says the contradictory state could only be reached through the REST settings endpoint or WP-CLI - the settings form never allowed it - and the routine re-enables the parent setting on any store caught in it, which the note describes as "so existing behavior carries over rather than changing silently". Until the routine runs, on such a store, it changes silently. (9.2.0 also renamed both settings in the interface: "Accept Manual Renewals" is now "Allow manual renewals at checkout".)
The plan selector stopped loading everywhere
The third change is not migrated, because it is not a setting:
The plan-selector script (wcsatt-frontend) used to load on every page, even ones that couldn't show subscription options. It's now registered but not enqueued.
If you render the plan-selector markup somewhere Subscriptions doesn't already handle - a custom template, an AJAX-loaded fragment, a quick-view modal - call WCS_ATT_Display::enqueue_frontend_script() before scripts print.
- same developer note
The script is what makes the "subscribe every month / every three months" selector update the price and the add-to-cart button. Since 9.2.0 it loads on single product pages that have plans, on the cart and checkout when the add-cart-to-subscription option is on, and - through a built-in integration - on every page of a store using the Flatsome theme, because "a quick view can open from any page". The changelog's fix line for Flatsome says quick-view plan selection works "again, as it did before 9.0.0". Since All Products for Subscriptions was folded into the plugin in June, the merged script had only wired up plan selectors that were on the page when it loaded; a modal opened from a listing page had nothing listening. 9.2.0 changes that guard so the script starts wherever it is loaded, and the Flatsome integration loads it everywhere, so on Flatsome the modal had been dead for two releases and is now alive.
On any other theme with a quick-view modal, whether it ever worked depended on the theme calling the plugin's initialiser after the modal's content arrived. What 9.2.0 changes is that the script is no longer on listing pages at all, so a theme that had wired it now has nothing to wire: a customer who picks a plan inside the modal sees the price not change. No error is logged; the selector is simply markup without its script.
Rolling back overwrites a setting
The fourth is for anyone who decides, on discovering any of the above, to go back to 9.1.0. The release adds proration for physical products, with new values in a setting that the older version does not know:
On the 9.1.0 settings screen, the dropdown has no option matching the stored value, so nothing appears selected. If an admin saves that settings section without picking a new value, the stored physical or physical-upgrade is overwritten with no.
- same developer note
A rollback followed by a routine visit to the settings tab and a click on Save
- to change something unrelated on the same screen - turns proration off, and 9.2.0 reinstalled afterwards reads "no" as the store's choice.
An administrator's login, then four checks
- Find out how the update was applied, then log in as an administrator. If
9.2.0 arrived by auto-update, from the command line, or through a hosting
panel, the upgrade routine has not run until an account that can activate
plugins loads a page or signs an API request. If your workflows use an
admin-level key, it has most likely run already; if not, open wp-admin with
one. To confirm the gifting migration
was scheduled, look under Tools → Scheduled Actions for
woocommerce_subscriptions_migrate_gifting_products; if the store had gifting set to "enabled for all products", the batches appear there. - If gifting was on for all products, decide what new products should do. They default to off. The product editor has the per-product setting, and the Products list has a field for it in the Bulk Edit panel for many at once. Add a product to a checklist for whoever creates them; there is no store-wide default to restore.
- If the theme has a quick-view modal and is not Flatsome, open one on a
subscription product and change the plan. If the price does not follow,
the script is missing on listing pages. The fix is the call to
WCS_ATT_Display::enqueue_frontend_script()the note describes, from the theme or a small plugin, plus - if the theme never did it - triggering the plugin'swcsatt-initializeevent once the modal has loaded; still small, for whoever maintains the theme. - Search custom code for
wcs_can_items_be_removed. A hit means the code used it as a permission check. Since 9.2.0 it returnsfalsefor everyone except the subscription's owner, including administrators, and its filter can no longer grant. The documented replacement is theuser_has_capfilter. - Do not roll back and then save the settings tab. If a rollback is unavoidable, note the proration value first, and re-enter it after reinstalling 9.2.0.
Steps 3 and 4 are code, and small; if the theme and the customisations were built by people no longer around, a website fix covers both, priced up front. Steps 1, 2 and 5 are not code, and are the ones that matter for most stores.
Read after 23 October
On 12 September the plugin's changelog had 9.2.0 as its latest entry, and no public report of a regression in it existed - which for a paid plugin without a public tracker is a weak signal either way. Subscriptions has shipped a release every 17 to 34 days this summer, so by the time this is published there will likely be a 9.2.x or a 9.3.0, and the upgrader gate, the migration and the script change should be checked against the version actually installed.
Sources
- WooCommerce Subscriptions 9.2.0: settings updates and dev changes - WooCommerce Developer Blog, published 8 September 2026, modified 9 September 2026 according to the page's metadata, read 12 September 2026. Every paragraph quoted above.
- Security update for WooCommerce Subscriptions - WooCommerce Developer Blog, 5 August 2026, read 12 September 2026. The 9.1.0 advisory.
- WooCommerce Subscriptions changelog - read 12 September 2026. The 9.2.0 entry of 8 September 2026 as the latest, the "show the remove item control" line, the "Undo?" line, the Flatsome fix line, and the release dates of 9.0.0 (23 June), 9.0.1 (10 July) and 9.1.0 (5 August).
- WooCommerce Subscriptions source at 9.1.0 and 9.2.0 - read 12 September 2026 from the pronamic/woocommerce-subscriptions mirror on GitHub (tags v9.1.0 and v9.2.0; the mirror's changelog matches the official one), through the GitHub API.
includes/core/wcs-functions.phplines 745-759 (9.1.0) and 745-789 (9.2.0) for the function and its docblock;includes/core/wcs-user-functions.phplines 423-430 for the capability grant;includes/api/class-wc-rest-subscriptions-controller.phplines 370-379 for the REST removal path;includes/core/upgrades/class-wc-subscriptions-upgrader.phplines 98-100 for the gate;includes/core/upgrades/class-wcs-plugin-upgrade-9-2-0.phpfor the four steps, the batch size, the product-ID ceiling and the "accepted trade-off" docblock;includes/apfs/class-wcs-att-display.phpandincludes/apfs/integrations/class-wcs-att-integration-fs.phpfor the script change and the Flatsome integration;includes/core/class-wcs-remove-item.phpfor the nonce actions; thetemplates/emails/directory for the absence of removal and resubscribe links. - WooCommerce Subscriptions REST API documentation - read 12 September 2026.
line_itemsas a writable field on update. - WooCommerce.node.ts - n8n source, read 12 September 2026 on master. The customer, order and product resources and no subscription resource.
- WooCommerce - Make app documentation, read 12 September 2026. The module list.
- WooCommerce integrations on Zapier and Subscription resource - WooCommerce Zapier documentation, read 12 September 2026. The paid-extension requirement and "Removed Line Items" as always ignored.
- wp_nonce_tick and wp_verify_nonce - WordPress source, read 12 September 2026. The one-day
nonce_lifedefault, the half-life tick, and the user and session token in the hash. - GitHub issue search, 12 September 2026, through the GitHub API - for
wcs_can_items_be_removed,_subscription_giftingand "subscriptions 9.2.0" across public repositories, including woocommerce/woocommerce and Automattic/woocommerce-payments. No report concerning this release.
Site down, or broken since the last update? Fix S — $300, 2 business days, fixed price.
Get my quote in 24hWritten by the Fixmation team.