Start with the table. If your symptom is not in it, run the command again with -v — the scoper
prints the configuration it resolved and every process it spawns, and that is usually the answer.
If you are coming from 3.2 or earlier, several long-standing failure modes were fixed in 4.0 rather than worked around. Check Upgrading to 4.0 before debugging.
| Symptom | Cause | Fix |
|---|---|---|
composer install succeeds, no deps/ folder, no output at all |
The plugin is not allowed to run | composer config allow-plugins.wpify/scoper true, or composer global config --no-plugins allow-plugins.wpify/scoper true for a global install. Composer skips disallowed plugins silently. |
extra.wpify-scoper.prefix is missing in … |
No prefix, or a typo in the key | Add a valid namespace. See prefix. |
… is not a valid PHP namespace |
Hyphens, spaces, a leading digit, or a leading/trailing \ in the prefix |
Use identifiers separated by \\, e.g. MyPlugin\\Deps. |
unknown extra.wpify-scoper.globals entry "…" |
A typo in globals |
Valid values: wordpress, woocommerce, action-scheduler, wp-cli. |
"plugin-update-checker" is deprecated and ignored |
globals still lists it |
Remove the line. PUC is scoped like any other dependency now. |
composerlock points at the manifest … |
composerjson and composerlock resolve to the same file |
Point composerlock somewhere else. The run publishes the lock over that path and would destroy your manifest. |
Class "…\WP_Query" not found at runtime |
A WordPress symbol got scoped | See A WordPress symbol got prefixed. |
no extra.wpify-scoper block in composer.json, nothing to scope |
You ran composer wpify-scoper in a project that does not configure the scoper |
Run it from the project root, or add the block. |
unknown action "…", expected install or update |
A typo in the command | composer wpify-scoper install or composer wpify-scoper update. |
tmp-XXXXXXXXXX/ left in the project root |
The run failed or was killed | See A tmp- directory was left behind. |
php-scoper was not found |
wpify/php-scoper is missing from the install |
Reinstall the plugin. The message lists every path that was tried. |
the Composer binary could not be located |
The deprecated bin/wpify-scoper was driven from a pipeline with no composer on PATH |
Use composer wpify-scoper install, or set COMPOSER_BINARY. |
already running (WPIFY_SCOPER_RUNNING is set), skipping this nested invocation |
A scoping run was triggered from inside a scoping run | See Nested invocation warning. |
| A vendored library breaks after scoping | It builds class names dynamically, so php-scoper cannot see them | Write a patcher — see Customizing php-scoper. |
| Plugin Check reports errors inside the scoped tree, or a WordPress.org submission is blocked by them | The scoped tree is in deps/, which Plugin Check scans |
See Plugin Check flags my scoped dependencies. |
Class "MyPlugin\Deps\…" not found on a WordPress.org release only |
The release build stripped vendor/, and the scoped tree was inside it |
See The scoped tree is missing from the release. |
| Two developers get different scoped output from the same commit | Different scoper versions | The scoper version is not in any lock file. Pin the same constraint everywhere; see Installing. |
The single most common report, and it is almost never the scoper.
composer install
# ...no wpify-scoper line, no deps/ folder, exit code 0Composer refuses to load plugins that have not been explicitly allowed, and it says nothing when it does so. Check:
composer config allow-plugins.wpify/scoperIf that is not true, set it — for a global install, in the global config:
composer global config --no-plugins allow-plugins.wpify/scoper trueIf the plugin is allowed and still nothing happens, the second cause is a missing
extra.wpify-scoper block. The scoper deliberately stays a complete no-op in projects that do not
configure it, because a global install activates it for every project on the machine.
Third: "autorun": false. Run composer wpify-scoper install explicitly.
Confirm all three at once with -v. If the configuration line does not appear, the plugin is not
running; if it does, the plugin is running and the problem is later in the pipeline.
PHP Fatal error: Uncaught Error: Class "MyPlugin\Deps\WP_Query" not found
Something WordPress declares was moved into your namespace. Three causes:
globalsis missingwordpress. Check the block; the default includes it, so this only happens when the key was set by hand.- A typo in
globals."wordpess"produces a warning and is then ignored, leaving you with no WordPress exclusions at all. Re-read the install output. - Your WordPress is newer than the shipped symbol list. The lists are regenerated weekly, but
a symbol added in a WordPress release newer than your scoper will be prefixed. Update
wpify/scoperand re-scope.
For a symbol that is genuinely missing from the lists, open an issue — that is a bug in the symbol extraction, and it is the most serious class of bug this project has.
For a symbol WordPress does not declare but you still need unprefixed, use
scoper.custom.php.
FILE: deps/guzzlehttp/guzzle/src/Handler/CurlFactory.php
ERROR curl_setopt() is discouraged. Use wp_remote_get() instead.
Plugin Check never scans vendor, vendor_prefixed or vendor-prefixed. It does scan deps,
which is this plugin's default output folder, so a scoped Guzzle is read as code you wrote.
There is nothing to fix in the library — any edit is undone by the next run. Move the tree instead:
"folder": "vendor-prefixed"then update the require_once in your plugin file to match, add /vendor-prefixed/ to
.gitignore, and re-scope. Since October 2024 an error here blocks a new WordPress.org submission
outright, so this is worth getting right before you submit rather than after.
Publishing to WordPress.org covers the move and what the exemption does not
buy you.
PHP Fatal error: Uncaught Error: Class "MyPlugin\Deps\GuzzleHttp\Client" not found
on a published build, when the same commit works locally. The scoped tree is a build artifact and the release does not have it. Two causes:
- The build never ran. WordPress.org SVN and plain git deploys do not run Composer. The
release step has to
composer install --no-dev --optimize-autoloaderfirst — that populatesvendor/and fires thepost-install-cmdthat writes the scoped tree. - Something stripped the directory.
.distignore,export-ignorein.gitattributes, anrsync --exclude, azip -x.vendor-prefixed/has to ship, and so doesvendor/— the plugin needs Composer's autoloader too.
Confirm by listing the published artifact rather than your working copy:
unzip -l my-plugin.zip | grep scoper-autoloadA successful run removes its workspace. A failed one keeps it on purpose:
wpify-scoper: the run failed, the workspace /srv/my-plugin/tmp-a1b2c3d4e5 was kept
- remove it once you no longer need it.
Inside it you may find deps-backup-<pid>/, which holds your previous deps/ folder. The swap
moves the old tree aside before installing the new one, so an interrupted run does not leave you
with no dependencies at all.
ls tmp-a1b2c3d4e5/
# deps-backup-41234/ destination/ source/ scoper.inc.php scoper.config.phpRecover whatever you need, then delete the directory. Add tmp-* to .gitignore so a leftover
never gets committed.
wpify-scoper: already running (WPIFY_SCOPER_RUNNING is set), skipping this nested invocation.
Remove extra.wpify-scoper from your scoped manifest to silence this.
The scoper sets WPIFY_SCOPER_RUNNING for the duration of a run, so the nested Composer — which
loads the globally installed copy of the plugin — cannot start a run of its own. Seeing this
warning means something asked for a second run from inside the first.
Usually: your composer-deps.json carries an extra.wpify-scoper block. It should not. Remove it.
Any release that changes what lands in deps/ can break a site with no warning inside your
project — nothing records which scoper version produced the tree.
When output changes unexpectedly:
composer global show wpify/scoper(orcomposer show wpify/scoper) on both machines.- Compare against CHANGELOG.md. Anything that alters generated output is at least a minor release.
- Re-scope everywhere and test the result before shipping it.
Pin the same constraint on every machine and in CI. See Installing.
Run the failing command with -vvv and open an issue with:
- the
wpify/scoperversion and how it is installed (global orrequire-dev) - your PHP version
- your
extra.wpify-scoperblock - your
composer-deps.json - the full
-vvvoutput
https://github.com/wpify/scoper/issues
Security issues do not belong in a public issue — see SECURITY.md.