By Greg Nowak. Last updated 2026-07-13.
When Drush fails with a Symfony or PHP fatal error, the stack trace can make the problem look deeper than it is. Often, the command is loading Drush from one installation and Symfony from another. A global Drush binary, a project’s vendor directory, and different PHP runtimes can combine into a toolchain that Composer never resolved or tested as one unit.
For an operations lead or agency team, this is more than a developer inconvenience. Drush may be part of deployments, cache rebuilds, database updates, cron tasks, and recovery procedures. The goal is therefore not just to silence today’s error. It is to restore a repeatable command-line environment.
What the EventDispatcher error is telling you
A fatal error involving Symfony\Component\EventDispatcher\EventDispatcher::dispatch() is characteristic of incompatible callers and components. Symfony changed the argument order of dispatch() during the Symfony 4.3 transition, so older tools paired with newer EventDispatcher code could fail immediately.
That history explains the symptom, but it does not prove that every EventDispatcher error has the same cause. Read the file paths in the stack trace. If they mix a global location such as /usr/local/bin or Composer’s global home with the Drupal project’s vendor directory, investigate the binary mismatch first. If every path belongs to the project, a contributed package or custom command may instead contain the incompatible call.
Diagnose before changing dependencies
Start in the directory containing the project’s composer.json. Run non-destructive checks before installing, removing, or upgrading anything:
pwd
command -v drush
php -v
composer config --global home
composer show drush/drush
vendor/bin/drush version
vendor/bin/drush status
composer why symfony/event-dispatcher
composer check-platform-reqscommand -v drush identifies the executable selected by the shell. The two vendor/bin/drush commands test the project copy explicitly. Composer’s why command shows which packages require EventDispatcher, while check-platform-reqs checks the real PHP runtime and extensions rather than relying on Composer’s configured platform value.
| What you find | Likely issue | Next action |
|---|---|---|
Project Drush works; plain drush fails |
Global binary or shell path | Update scripts to call vendor/bin/drush |
| Both commands fail | Project dependency or PHP problem | Inspect Composer blockers and platform requirements |
| Only production fails | Environment drift | Compare PHP, extensions, lock file, and deployment steps |
| All stack-trace paths are project-local | Package or custom-code incompatibility | Identify the caller before changing Symfony |
The safest modern fix
If vendor/bin/drush status works, the immediate fix is operational: replace global drush calls in CI, deployment hooks, cron entries, and runbooks with the project binary. Do not leave production automation dependent on whichever tool happens to appear first in a user’s PATH.
If Drush is not a project dependency, add it in a development branch or controlled build environment—not as an improvised production-server change:
composer require drush/drush
vendor/bin/drush status
composer validate
git diff -- composer.json composer.lockComposer will choose a suitable constraint when none is supplied, but that does not remove the need to review the result. Test the site’s normal Drush commands, commit both Composer files, and deploy the lock file through the established release process. If the proposed major cannot be installed, ask Composer for the blocker chain:
composer prohibits drush/drush ^13 --tree
composer prohibits drush/drush ^14 --treeChoose a supported Drush line
As of 13 July 2026, the Drush compatibility table recommends Drush 13 for Drupal 10.2+ and Drupal 11, and Drush 14 for Drupal 11.3+ and Drupal 12. Both require PHP 8.3 or newer. Other combinations shown with a plain compatibility mark may still run, but Drush describes them as no longer supported.
Do not force a newer major with --ignore-platform-reqs, and do not patch the global Composer directory by installing individual Symfony packages. Those shortcuts hide the constraint that needs attention and can break another Drupal project using the same account.
Containing a legacy site
When a Drupal or PHP upgrade cannot happen immediately, contain the risk. Pin the compatible Drush major inside the repository, record the reason, isolate the site’s runtime, and add the upgrade dependency to the delivery plan. Compatibility can keep an urgent release moving; it should not be presented to stakeholders as a supported long-term platform.
Prevent the next failure
- Keep Drush in each project’s
composer.jsonandcomposer.lock. - Use
vendor/bin/drushconsistently in local, CI, staging, and production workflows. - Run
composer check-platform-reqsduring builds and stop deployment when it fails. - Review Drupal, Drush, and PHP compatibility together before approving upgrades.
- Remove undocumented global packages and server-specific command aliases.
The technical fix is usually small; the lasting improvement is making the repository the source of truth for the command-line toolchain. If inherited Drupal environments or fragile deployment scripts are consuming too much agency time, Greg can help turn them into a documented, supportable delivery setup.
Related on GrN.dk
- Drupal CMS 2.0 Speeds Marketing Site Rebuilds, but It Is Not Autopilot
- Drupal's Automatic Updates Cleanup Got More Urgent After the Old API Shutdown
- Form Spam Is a Lead-Quality Problem: A Practical Hardening Playbook
Need help with this kind of work?
Talk to Greg about Drupal delivery Get in touch with Greg.