This blog has been quiet since 2022, and the last post about WordPress here is from 2019. I thought about publishing this on Webdados or Naked Cat Plugins instead. But this is my opinion as a developer about a bug in WordPress core and a decision about my own products, so it belongs here.

I sell extensions for WooCommerce at Naked Cat Plugins. Most of them add features to one of my free plugins on WordPress.org: Shop as Client for WooCommerce is free, and the Shop as Client PRO add-on builds on it. Since WordPress 6.5, the PRO add-ons declare the free plugin in the Requires Plugins header, so WordPress won’t activate one without the other. That works on the plugins list. But a customer who uploads the PRO zip sees an Activate button that leads to an error page. This is how I found that, what I weighed before deciding what to do about it, and why I’m keeping the header for now.

How I got here

On 23 September 2026, Rodolfo Melogli asked on X: “WP plugin devs: is it not bad UX when a PRO plugin requires a free .org companion plugin to be installed first?”

Steve Jones answered with what several developers in the thread said they do: make the free plugin a dependency and, if Pro is installed without it, auto-install and activate it. Rodolfo replied with the screen he didn’t want customers to see:

WordPress error page: Sales by State Report Pro for WooCommerce requires 1 plugin to be installed and activated: Sales by State Report for WooCommerce, with a Manage plugins link. Query Monitor shows the message came from WordPress core via wp_die()

My first reaction was that this can’t happen. Since WordPress 6.5, a plugin with the Requires Plugins header can’t be activated while its dependencies are missing: the Activate link on the plugins list is disabled. So auto-installing shouldn’t be needed. I asked how he got there, with this screenshot:

Plugins list row for AutomateWoo - Manual Trigger Users, with the Activate link greyed out, Requires: AutomateWoo, WooCommerce, and the notice: This plugin cannot be activated because required plugins are missing or inactive

His answer: the customer buys Pro, installs and activates it on their site, and gets the wp_die() error because the free plugin isn’t there. So I tried it with the Shop as Client PRO add-on, uploading the zip on a site without the free plugin. The upload screen offers “Activate Plugin”, while the plugins list, for the same plugin, doesn’t:

WordPress upload screen after installing shop-as-client-pro.zip: Plugin installed successfully, with an Activate Plugin button and a Go to Plugin Installer link
Plugins list row for the Shop as Client PRO add-on with Activate disabled, Requires: Shop as Client for WooCommerce and WooCommerce, and the notice: This plugin cannot be activated because required plugins are missing or inactive

That’s a WordPress bug. I reported it back in the thread, and Rodolfo said he would report it to WordPress core.

The bug

The plugins list and the upload screen disagree about the same plugin. Checked against WordPress 7.2-alpha:

  • Plugins list (class-wp-plugins-list-table.php): a plugin with unmet dependencies gets a disabled Activate link, a disabled checkbox so bulk actions can’t reach it either, and the notice “This plugin cannot be activated because required plugins are missing or inactive.” Each name on its Requires line opens the plugin install modal, where a WordPress.org plugin can be installed and activated.
  • Upload screen (Plugin_Installer_Skin::after()): builds the “Activate Plugin” button checking only the user’s permissions and whether the plugin is already active. It never looks at dependencies.
  • Activation (validate_plugin_requirements() in wp-admin/includes/plugin.php): does check dependencies, and returns an error that plugins.php shows with wp_die(). That’s the page in Rodolfo’s screenshot.

Nothing breaks: the plugin is never activated with its dependency missing. But the button the customer is given leads to a dead end: a bare error page with a “Manage plugins” link. That’s the scenario Rodolfo described: a new customer’s first step after paying is to install what they bought, and this is the screen they get. It’s a functional bug, not a cosmetic one. The fix belongs in core: the upload screen should apply the same check the plugins list already does.

A plugin author can’t work around it from the Pro plugin: WordPress doesn’t load an inactive plugin’s code, so nothing in Pro runs during its own upload. A filter on the upload screen’s buttons (install_plugin_complete_actions) would have to live in a plugin that is already active.

Keep the header, or auto-install?

Until core fixes the upload screen, the header can’t give a customer who uploads Pro a good first screen. The only way around it is to remove the free plugin from Requires Plugins so Pro can activate, and have Pro install the free plugin itself. So I looked at what that costs.

Shop as Client Pro already had most of the safety net, because its minimum WordPress version is 6.4 and 6.4 ignores the header. On plugins_loaded it checks that the free plugin is loaded and recent enough, and that WooCommerce is loaded and recent enough. If either fails, Pro loads nothing and shows an admin notice. Without the header, that check does all the work.

What auto-installing gains

  • Upload, Activate, working plugin. The customer never sees an error page.
  • It can also update the free plugin when it’s older than Pro needs. The header only checks that the plugin exists, not its version.
  • Customers who already have the free plugin see no difference.

What it costs

  • Core no longer protects the free plugin. With the header, WordPress won’t let anyone deactivate or delete it while Pro is active. Without it, Pro has to cope: go quiet, show a notice with a Reactivate button.
  • The Requires and Required by lines disappear from the plugins list, unless you add them back yourself.
  • The install will sometimes fail, and each case needs a fallback:
    • DISALLOW_FILE_MODS is set.
    • The host needs FTP credentials instead of writing files directly.
    • The user can’t install plugins (for example, a site admin on multisite who isn’t a super admin).
    • The server can’t reach WordPress.org.
  • Timing. The activation hook is the wrong place: any output there triggers WordPress’s “unexpected output” warning, and there’s no way to ask for FTP credentials. It has to run on the next admin page load, before the redirect to the settings page (which belongs to the free plugin, so the order matters).
  • Edge cases. Bulk activation and WP-CLI skip that redirect, so the install happens on the next admin page. On multisite, a network-activated Pro needs the free plugin network-activated too.
  • Consent. The customer clicked Activate on a plugin that can’t work without the free one, so installing it is defensible. Pro should still say what it did: “Shop as Client was installed and activated because the PRO add-on needs it.”
  • Code to maintain. The header is one line. Doing this properly is a small class with its own notices and tests.

When the install can’t run, the fallback is a notice whose button points at core’s own install screen (update.php?action=install-plugin&plugin=shop-as-client, with the nonce). That screen knows how to ask for FTP credentials, so the worst case is one extra click through a normal WordPress screen, never an error page.

Side by side

The header wins on everything except the customer’s first minute after uploading, which is exactly where the bug is. “Auto-install” here means doing it properly, with the checks and fallbacks from the previous section.

SituationRequires Plugins headerAuto-install from Pro
Customer uploads the Pro zip and clicks Activate; free plugin missingFails. Error page from wp_die() (the core bug)Works. Pro activates, installs, and activates the free plugin
Customer installs Pro, then activates it from the plugins listWorks. Activate is disabled; the row explains why and links to the install modalWorks. Pro activates and installs the free plugin
Someone later deactivates or deletes the free pluginBlocked by core. Deactivate and Delete are disabled while Pro is activeAllowed. Pro has to go quiet and show a notice
Free plugin installed but older than Pro needsNot checked. Pro’s own check has to catch itCan update it, if the code does it
WordPress 6.4 or olderIgnored. Pro’s own check has to catch itWorks
Host needs FTP credentialsWorks. Core’s install screens ask for themSilent install impossible. Falls back to a notice linking to core’s install screen
DISALLOW_FILE_MODS, or a user who can’t install pluginsClear message. Nothing can be installed anywayCan’t install. The notice has to say so, or Pro just looks broken
WordPress.org unreachableInstall modal fails with core’s own errorDownload fails. Needs a notice
Requires and Required by lines on the plugins listYes, from coreNo, unless added by hand
Code to maintainOne header lineA class with notices, fallbacks and tests

So auto-install trades one broken screen for about half a dozen situations you now handle yourself. Whether that’s worth it depends on how many customers actually reach that screen.

An existing library: WP Dependency Installer

WP Dependency Installer by Andy Fragen is a drop-in class for exactly this: you list dependencies in a JSON file, and it installs and activates the required ones. Andy is also one of the authors of core’s Plugin Dependencies feature. I read the current version (last commit April 2025) to see which of the problems above it solves.

ProblemWhat the library does
Silent installSolved. A custom installer skin that prints nothing
Free plugin being deactivated or deletedPartly. Removes the Deactivate and Delete links and adds a “Required Plugin” label and a “Required by” line. Bulk actions can still deactivate it
Host needs FTP credentialsNot handled. No check for direct file access, so core’s credentials form would print in the middle of admin_init
DISALLOW_FILE_MODS, or a user who can’t install pluginsSilent. Both the install and the notice need update_plugins, so the customer sees nothing and no reason
Minimum version of the dependencyNot checked. Installed and active is enough
When the install runsOn any admin page load, for anyone who can update plugins. The nonce it checks is one it creates in the same request, so it doesn’t protect anything
MultisiteNetwork-activates only when the current page is in network admin

It also pulls in a second package (afragen/wp-dismiss-notice) and supports GitHub, Bitbucket, and GitLab sources that a WordPress.org dependency doesn’t need. The parts worth borrowing are the empty installer skin and the row-link filtering, about 40 lines. The gaps are in the failure cases, and those are the ones between a customer and a broken screen.

And WooCommerce?

Shop as Client Pro requires two plugins: the free Shop as Client and WooCommerce. If auto-installing the free one is fine, would you auto-install WooCommerce too? I wouldn’t.

The customer was told both things before buying: that this is a WooCommerce extension, and that it’s an add-on for the free plugin on WordPress.org. The difference is what each one is:

  • The free plugin is part of the product. Same author, same feature set, split in two because WordPress.org hosts the free half. Installing it completes what the customer bought. It changes nothing else about their site.
  • WooCommerce is the platform. Installing it turns a site into a shop: new database tables, pages, a setup wizard, emails, settings. Nobody should get that as a side effect of activating an add-on.
  • A missing WooCommerce usually means something else. The customer uploaded the plugin to the wrong site, or WooCommerce is deactivated on purpose (for example, to troubleshoot a conflict). Reactivating it would undo what they just did.

The same caution applies, on a smaller scale, to the free plugin itself. Installing it when it’s missing is safe. Activating it when it’s installed but inactive is a judgment call: most of the time it just hasn’t been activated yet, but someone may have turned it off deliberately. If Pro auto-installed, I’d activate it since the customer just activated Pro, and note that in the notice.

So WooCommerce stays in Requires Plugins. That means the upload bug can still reach a customer with no WooCommerce, but that’s rare for a WooCommerce extension, and the error message names the missing plugin. It also shows why the fix belongs in core: any plugin with a Requires Plugins header can hit it, whatever the author does about companion plugins.

Options, and where I landed

  1. Keep the header and set expectations. Say “install and activate the free plugin from WordPress.org first” on the product page, in the purchase email, and in the documentation. No code, but the customers who don’t read it still get the error page.
  2. Drop the free plugin from the header and auto-install it, with the checks and fallbacks above. WooCommerce stays in the header.
  3. Make Pro standalone, shipping the free plugin’s code inside it. No dependency at all. I won’t go that route. Most of my customers find the free plugin on WordPress.org, use it, and buy PRO when they need more. I also build PRO as an add-on on purpose: it doesn’t create its own settings or feature screens; it adds to the free plugin’s. Other developers can and will disagree with this. It’s a preference, not a rule.
  4. Wait for core to fix the upload screen. The right fix, but existing sites run older WordPress versions for years.

For Shop as Client PRO (and all my other paid add-ons), I’m keeping the Requires Plugins header, for now. Most of my customers come from the free plugin, so they already have it installed when they upload PRO, and the bug never shows. I have never had a support ticket about it. Auto-install would fix a problem I don’t know exists and take away core’s protection from every customer. I may revisit this if that changes.

The core bug should still be fixed. Until it is, every plugin using the header can put an error page in front of a customer who uploads it.

What I changed

After the thread on X and writing this post, I made the install order harder to miss for every Naked Cat Plugins PRO add-on that requires one of my free plugins on WordPress.org.

  • Documentation: where the add-on has a documentation page, the setup instructions now say to install and activate the free plugin first. A new question, “I uploaded the PRO add-on and got an error when activating it. What’s wrong?”, explains the error page, says nothing on the site was broken or changed, and gives the two steps to get past it.
  • Purchase note: every PRO add-on product, yearly and lifetime, now opens its purchase note with “Before installing: install and activate the free plugin from WordPress.org first”, linking to it. WooCommerce includes the purchase note in the order email, the same one that carries the license key and the download link, so it reaches the customer before they upload anything.

This is a real Shop as Client Pro order email, edited to show the new purchase note above “Getting started”:

Naked Cat Plugins order email for a Shop as Client PRO add-on renewal, with customer details blurred. Under the product, the purchase note reads: Before installing: install and activate the free Shop as Client for WooCommerce plugin from WordPress.org first. This PRO add-on can't be activated without it. Below it, the Getting started link to the documentation

I know people don’t read documentation, and many will still miss the purchase note in the email. But again, I’ve had zero support tickets about this so far. These are small text changes, not code: I’m not building a fix for a problem I haven’t seen.

Sources

Disclaimer: I wrote this post with Claude, working from the X thread, my own tests, and my decisions. I reviewed and edited everything.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *

Please copy the string 3pFLN9 to the field below: