Summary
There is no supported way to make a Freemius-powered premium update appear in WordPress core's update_plugins transient from a non-admin, non-cron context (WP-CLI, server-side update managers such as Plesk/cPanel WP Toolkit, and intermittently via remote management dashboards). As a result wp plugin update <slug> reports no update and installs nothing, and bulk update tooling shows stale or missing updates. For security releases this means sites silently stay unpatched.
Context
We are a WordPress agency managing many client sites and patching them centrally (WP-CLI and a remote management dashboard, on cPanel/CloudLinux). A recent security release of a Freemius-powered premium plugin (a fix for an RCE in older versions) had to be rolled out fleet-wide and fast. The standard tooling could not be trusted to apply it.
Observed behavior
wp plugin update <slug> reports the plugin is already up to date, or finds no update, even when a newer premium version exists for the license.
- Remote/bulk update tools intermittently fail to detect or apply the update.
- Server-side update managers (we have also seen this with Plesk/cPanel WP Toolkit) appear to be affected by the same root cause, since they run outside an admin session.
Why (from reading the SDK, current master)
- The updater registers
pre_set_site_transient_update_plugins_filter in includes/class-fs-plugin-updater.php, which calls $this->_fs->get_update( false, fs_request_get_bool( 'force-check' ) ). Outside an admin request, force-check is false, so get_update() is served from the local cache (UPDATES_CHECK_CACHE_EXPIRATION).
- The data sync that refreshes that cache is gated to an admin/non-cron context. In
includes/class-freemius.php: if ( $this->has_api_connectivity() && ! self::is_cron() && $this->is_user_in_admin() ) { ... }. Under WP-CLI, is_user_in_admin() is false and DOING_CRON is not defined, so the cache is never refreshed headlessly.
- Your documentation states the transient is populated "after visiting the Account page", and that updates "can only work when the product is actually active". There is no documented WP-CLI / headless path.
Workaround we had to use (illustrates the gap)
To get a fresh update into the transient under WP-CLI we had to, per site: load the upgrader includes, call a private method via reflection (get_update with the flush flag set to true) to force a fresh check, re-run pre_set_site_transient_update_plugins_filter manually and write the result back with set_site_transient('update_plugins', ...), then run the core Plugin_Upgrader. Because updates are served step-by-step, far-behind installs needed several passes. This is brittle and relies on private API.
Impact
Agencies that patch many sites centrally cannot rely on WP-CLI, server-side update managers (e.g. WP Toolkit), or remote management tooling to apply Freemius-powered updates. This is most serious for security releases: the mass-update tooling returns false or no-op results and sites stay vulnerable.
Request
A supported, public way to refresh and inject Freemius updates in headless contexts, for example any of:
- A first-class WP-CLI command (or documented recipe) to force a fresh update check and populate the
update_plugins transient.
- A public action/filter or method to force a fresh update check outside admin request params (so
wp plugin update works without reflection on private methods).
- Documentation of the supported approach for WP-CLI / remote management at scale.
Separately, the step-by-step delivery multiplies the number of passes needed for far-behind installs during a mass update. An option to jump straight to the latest release, at least for security releases, would materially help agencies patch quickly.
Environment
WP-CLI 2.x, WordPress 6.9, PHP 8.x, cPanel/CloudLinux, plugin bundling a current Freemius SDK 2.x. Happy to share detailed logs privately.
Summary
There is no supported way to make a Freemius-powered premium update appear in WordPress core's
update_pluginstransient from a non-admin, non-cron context (WP-CLI, server-side update managers such as Plesk/cPanel WP Toolkit, and intermittently via remote management dashboards). As a resultwp plugin update <slug>reports no update and installs nothing, and bulk update tooling shows stale or missing updates. For security releases this means sites silently stay unpatched.Context
We are a WordPress agency managing many client sites and patching them centrally (WP-CLI and a remote management dashboard, on cPanel/CloudLinux). A recent security release of a Freemius-powered premium plugin (a fix for an RCE in older versions) had to be rolled out fleet-wide and fast. The standard tooling could not be trusted to apply it.
Observed behavior
wp plugin update <slug>reports the plugin is already up to date, or finds no update, even when a newer premium version exists for the license.Why (from reading the SDK, current master)
pre_set_site_transient_update_plugins_filterinincludes/class-fs-plugin-updater.php, which calls$this->_fs->get_update( false, fs_request_get_bool( 'force-check' ) ). Outside an admin request,force-checkisfalse, soget_update()is served from the local cache (UPDATES_CHECK_CACHE_EXPIRATION).includes/class-freemius.php:if ( $this->has_api_connectivity() && ! self::is_cron() && $this->is_user_in_admin() ) { ... }. Under WP-CLI,is_user_in_admin()is false andDOING_CRONis not defined, so the cache is never refreshed headlessly.Workaround we had to use (illustrates the gap)
To get a fresh update into the transient under WP-CLI we had to, per site: load the upgrader includes, call a private method via reflection (
get_updatewith the flush flag set to true) to force a fresh check, re-runpre_set_site_transient_update_plugins_filtermanually and write the result back withset_site_transient('update_plugins', ...), then run the corePlugin_Upgrader. Because updates are served step-by-step, far-behind installs needed several passes. This is brittle and relies on private API.Impact
Agencies that patch many sites centrally cannot rely on WP-CLI, server-side update managers (e.g. WP Toolkit), or remote management tooling to apply Freemius-powered updates. This is most serious for security releases: the mass-update tooling returns false or no-op results and sites stay vulnerable.
Request
A supported, public way to refresh and inject Freemius updates in headless contexts, for example any of:
update_pluginstransient.wp plugin updateworks without reflection on private methods).Separately, the step-by-step delivery multiplies the number of passes needed for far-behind installs during a mass update. An option to jump straight to the latest release, at least for security releases, would materially help agencies patch quickly.
Environment
WP-CLI 2.x, WordPress 6.9, PHP 8.x, cPanel/CloudLinux, plugin bundling a current Freemius SDK 2.x. Happy to share detailed logs privately.