Skip to content

--skip-plugins is ignored on WordPress 7.0.4 and higher under specific circumstances. #548

Description

@brechtryckaert

Bug Report

Describe the current, buggy behavior
We have automated updates using WP-CLI on our hosting-cluster. We notice that since August 20th, this behaviour is occurring on a lot of sites of customer that have built their sites with a premium plugin but lack a valid license or have an expired license.

How it occurs
Every night we run several wp-cli commands to deploy updates of plugins and themes. We use WP-CLI for this, obviously.

# wp plugin update js_composer
Onderhoudsmodus inschakelen...
Warning: Update pakket niet beschikbaar.
Onderhoudsmodus uitschakelen...
+-------------+-------------+-------------+--------+
| name        | old_version | new_version | status |
+-------------+-------------+-------------+--------+
| js_composer | 8.3.1       | 9.0.1       | Error  |
+-------------+-------------+-------------+--------+
Error: No plugins updated (1 failed).

So at this point, to ensure we do not hit a blocking factor due to a plugin conflict, we'll repeat the command with the --skip-plugins option:

# wp plugin update js_composer --skip-plugins
Onderhoudsmodus inschakelen...
Warning: Update pakket niet beschikbaar.
Onderhoudsmodus deactiveren...
+-------------+-------------+-------------+--------+
| name        | old_version | new_version | status |
+-------------+-------------+-------------+--------+
| js_composer | 8.3.1       | 9.0.1       | Error  |
+-------------+-------------+-------------+--------+
Error: No plugins updated (1 failed).

Now here's the weird thing. If you then run that same command again, this suddenly declares that the plugin did update (even when it did not):

Success: Plugin already updated.

This only occurs on the second time you run the same command with the --skip-plugins option.

We have seen this occur consistently on several premium plugins in combination with WordPress 7.0.4 or 7.1. We did not see this behaviour in combination with earlier versions of core.

Describe how other contributors can replicate this bug

To replicate you'll need these things:

  • a premium plugin (WPBakery Page Builder, Elementor Pro) that has an update available, but has no valid license.
  • WordPress 7.0.4 or 7.1

How to replicate:
(for this example I'll assume you are also testing with js_composer, update your plugin name accordingly when testing with another plugin)

  • Run: wp plugin update js_composer
  • Run: wp plugin update js_composer --skip-plugins
  • Run: wp plugin update js_composer --skip-plugins

Expected outcome

The first two commands will result in:
Error: No plugins updated (1 failed).
The third command will result in:
Success: Plugin already updated.

Let us know what environment you are running this on

# wp cli info
OS:	Linux 6.1.182-hcl #deb11 SMP Fri Aug  7 06:55:11 UTC 2026 x86_64
Shell:	/bin/bash
PHP binary:	/usr/local/php-7.4/bin/php
PHP version:	7.4.33
php.ini used:	/data/jail/usr/local/php-7.4/etc/php.ini
MySQL binary:	/usr/local/bin/mysql
MySQL version:	mysql  Ver 14.14 Distrib 5.7.44-57, for debian-linux-gnu (x86_64) using  8.1
SQL modes:
WP-CLI root dir:	phar://wp-cli.phar/vendor/wp-cli/wp-cli
WP-CLI vendor dir:	phar://wp-cli.phar/vendor
WP_CLI phar path:	phar:///usr/bin/wp
WP-CLI packages dir:	/data/sites/web/REDACTED/.wp-cli/packages/
WP-CLI cache dir:	/data/sites/web/REDACTED/.wp-cli/cache
WP-CLI global config:
WP-CLI project config:
WP-CLI version:	2.12.0

Sadly I'm not able to provide a solution just yet, as I am still trying to figure out where the cause originates.

Activity

  1. swissspidy commented on Sep 4, 2026

    @swissspidy
    Member

    I think this is less about --skip-plugins not working, but more about transients changing between update runs, as a different set of plugins is running.

    We're working on WP-CLI v3 which contains a bunch of improvements in this area (see below). I'd recommend keeping an eye on this again after updating. You may also try WP-CLI nightly to test it already now.

    Related:

  2. ekamran commented on Sep 12, 2026

    @ekamran
    Contributor

    I reproduced this without any premium plugin or license, and narrowed it down, so sharing the full picture.

    Repro fixture: a 20 line plugin whose only job is to mimic an unlicensed premium updater, it filters pre_set_site_transient_update_plugins and injects an update entry with an empty package. With that active, the reported sequence reproduces exactly: wp plugin update premium-sim fails honestly, the first --skip-plugins run fails honestly, and the second --skip-plugins run prints Success: Plugin already updated. while the old version is still installed.

    Mechanism, verified by dumping the stored transient between runs: the first --skip-plugins run itself rewrites the transient, the entry is gone and last_checked is bumped during that run. The failed bulk update still reaches plugin update-cache cleanup, and the upgrader_process_complete path can immediately call wp_update_plugins() with timeout 0. With --skip-plugins, that refetch happens without the premium updater code, so the refreshed transient loses the premium update entry. The next run reads that transient, finds no update for the plugin, and reports it as already updated.

    I also checked several real premium products locally to see how representative the fixture is. Both updater architectures are in use: some hook pre_set_site_transient_update_plugins, the write-time shape the fixture models, Elementor Pro among them, some inject only at read time through site_transient_update_plugins, and one hooks both. A theme with the same two hooks on update_themes shows --skip-themes carries the same shapes. The two classes fail differently under skip flags: write-time entries survive in the stored transient until a failed skipped run rewrites it, which is the three run sequence above, while read-time entries are never in the stored transient at all, so the false already updated can appear on the first skipped run. I observed exactly that once with one extension, its update showed in a fresh loaded check while --skip-plugins reported already updated, but its update API became flaky before I could script a reliable repeat. Both classes converge on the same gap: with skip flags active, the requested plugin or theme can be absent from both response and no_update while a skipped updater would have supplied an update.

    On the version claim: I could not confirm the 7.0.4 boundary. The identical sequence behaves the same way on a clean WordPress 7.0.3 official Docker image, and the 7.0.4 release contains no changes to the update or upgrader code paths, it was an Imagick security release. So the observation may have started around August 20, but a 7.0.4 core change is probably not the cause.

    Also tested on the current v3 alpha build from the framework main branch: same false success, so the v3 improvements do not seem to cover this case.

    One possible direction: when --skip-plugins or --skip-themes is active and the requested plugin or theme appears in neither response nor no_update, the update command could warn that the update status is unknown because the plugin's own code is skipped, instead of reporting success. That path is shared with real no-update cases, so it needs careful implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions