If you are seeing a fatal error around EventDispatcher::dispatch() when running Drush, treat it as a toolchain problem first, not a content problem. The usual failure is simple: the Drush binary being executed was installed in one place, while the Drupal site is loading Symfony components from another. That mismatch is common on inherited servers, shared agency boxes, and developer machines with old global Composer installs. For a business owner or operations lead, the real issue is not the stack trace. It is that routine maintenance, deployments, or recovery commands can stop working at exactly the wrong time.
The good news is that the fix is usually straightforward. The outdated habit was to keep adding Symfony packages to Composer global until the error disappeared. That can temporarily silence one site and break the next. The current safe model is to install Drush inside each Drupal project, run that copy, and let the site’s composer.lock define the dependency set.
What this error usually means
The classic symptom is a fatal error mentioning Symfony\Component\EventDispatcher\EventDispatcher::dispatch(). This started showing up when Symfony changed the dispatch() signature and older tooling expected a different call pattern. If your stack trace mixes a global Drush path such as /usr/local/drush or a Composer home directory with the site’s own /vendor/symfony/..., you are effectively cross-wiring two dependency trees.
That is why one Drupal site can work while another fails on the same server. It is also why quick fixes in a shared global Composer directory age badly. Global Composer state is shared per user account, so one emergency change can quietly alter the CLI environment for every other project that user touches.
The safest fix for a live Drupal project
Install Drush as a project dependency and run the project binary from the site root. That keeps Drush aligned with the Drupal, PHP, and Symfony versions the site is actually using.
composer require drush/drush
vendor/bin/drush status
vendor/bin/drush versionIf your team prefers a shorter command, use Composer’s vendored-binary execution support or add ./vendor/bin to the shell path only in the project context. The important part is not the command length. It is that automation, deploy hooks, cron jobs, and emergency runbooks stop depending on whatever happens to live in /usr/local/bin or Composer global.
If the site is older, do not blindly pull the newest Drush major. Match the project to a compatible Drush version inside the repository instead of relying on a global install. As of June 11, 2026, the current Drush compatibility table lists Drush 13 as supported for Drupal 10.2+ and Drush 14 as supported for Drupal 11.3+.
For production teams, make the change complete: commit the updated composer.json and composer.lock, update deployment scripts to call the repo-local binary, and remove undocumented server-level Drush shortcuts. Partial fixes are how the same outage returns three months later during a rushed release window.
How to diagnose the mismatch before you touch versions
If the site is old or the stack has been inherited, check where Drush is coming from and what is blocking a clean upgrade path before you pin anything.
which drush
vendor/bin/drush version
composer config --global home
composer why symfony/event-dispatcher
composer prohibits drush/drush ^13
composer check-platform-reqsThese commands answer different questions. which drush shows whether your shell is resolving a global binary. vendor/bin/drush version confirms the project copy. composer config --global home shows the shared Composer home that may be affecting your workflow. composer why and composer prohibits show what currently requires or blocks the package versions you want. composer check-platform-reqs helps you separate a dependency problem from a PHP runtime problem.
If you are targeting a different Drush major, change the version constraint in the prohibits command. The point is to inspect the blocker chain first, not to guess at package pins until Composer stops complaining.
If you are stuck on legacy Drupal for now
Sometimes a clean upgrade is not immediately possible. A site may be tied to an older Drupal major, a hosting image you do not control, or a release process nobody wants to touch mid-campaign. In that situation, the practical goal is containment.
Containment means matching a compatible Drush major inside the project, documenting that choice, and treating it as transitional. Current Drush guidance is explicit: older Drush lines may still be compatible with some legacy Drupal versions, but they are no longer supported. That distinction matters. Compatible may get tonight’s deployment out the door; it does not make the stack low-risk.
What you should avoid is rebuilding the old global workaround by hand with commands like composer global require symfony/event-dispatcher ... one package at a time. That approach can leave one server able to run one site’s commands while breaking another site on the same account. If a global workaround is unavoidable for a short period, isolate it, document it, and plan its removal.
What to standardise across teams
- Keep Drush inside each Drupal repository rather than in a shared global toolchain.
- Call
vendor/bin/drushin CI, deploy scripts, cron, and incident runbooks. - Check Drupal, PHP, and Drush compatibility before upgrades, not after a fatal error.
- Use Composer diagnostics to understand blockers before changing constraints.
- Remove undocumented server fixes that make environments behave differently from the repository.
This class of error looks like low-level PHP noise, but the management problem is dependency governance. Once Drush is tied to the project and the upgrade path is documented, support becomes more predictable, handovers get easier, and routine maintenance stops turning into forensic work.
If your team is dealing with mixed Drupal versions, fragile deployment scripts, or inherited server fixes, I can help turn that into a supportable operating model without unnecessary platform churn. Talk to Greg about stabilising your Drupal delivery setup.
Need help with this kind of work?
Talk to Greg about stabilising Drupal operations Get in touch with Greg.