Drush and Symfony Errors in Drupal: A Safe, Practical Fix

Illustrated infographic summarizing: Drush Symfony PHP Errors in Drupal: A Safe, Practical Fix

By Greg Nowak. Updated 21 September 2026.

A Drush command that ends in a Symfony or PHP fatal error can look like a deep Drupal failure. Quite often, it is a toolchain problem: the shell starts one Drush installation, that process loads dependencies from somewhere else, and a different PHP runtime completes the mismatch.

That distinction matters when Drush runs database updates, cache rebuilds, cron jobs or deployment hooks. The aim is not merely to make one command pass. It is to restore a predictable environment that developers, CI and production all run the same way.

What an EventDispatcher error actually tells you

An error involving Symfony\Component\EventDispatcher\EventDispatcher::dispatch() points to an incompatible caller and component. Symfony changed the method’s argument order during the Symfony 4.3 transition, with the newer signature becoming the standard in Symfony 5. That history explains why old Drush releases, contributed commands or custom code can fail against newer Symfony packages.

It does not prove that Symfony itself should be downgraded. Start with the file paths in the stack trace. A mixture of global Composer directories and the project’s vendor directory strongly suggests that the wrong Drush executable was selected. If every path is inside the project, investigate its resolved dependencies and custom command code instead.

Diagnose the runtime before changing it

Work from the directory containing composer.json. These checks are non-destructive:

pwd
command -v php
command -v composer
command -v drush
php -v
php --ini
composer --version
composer config --global home
composer show drush/drush
vendor/bin/drush version
vendor/bin/drush status
composer why symfony/event-dispatcher --tree
composer check-platform-reqs

command -v reveals what the shell will execute. Calling vendor/bin/drush bypasses a global Drush installation and tests the copy resolved with the project. Composer’s why command shows which packages introduced EventDispatcher. check-platform-reqs checks the real PHP version and extensions, ignoring any simulated config.platform value.

Result Likely cause Best next move
vendor/bin/drush works, plain drush fails Global binary or shell-path mismatch Change scripts and runbooks to use the project binary
Both Drush commands fail Project dependency, extension or PHP mismatch Inspect Composer’s dependency tree and platform checks
Only production fails Environment or release drift Compare PHP, extensions, lock file and install flags
All failing paths are project-local Unsupported package or custom command Identify the incompatible caller before changing Symfony
A decision matrix for separating executable-path mistakes from genuine project incompatibilities.

If the project Drush works, fix the invocation

Replace unqualified drush calls in deployment scripts, scheduled jobs, CI configuration and operating notes with vendor/bin/drush. A convenient developer alias is fine, but production automation should not depend on whichever executable appears first in a user’s PATH.

Also confirm that cron and deployment users run the intended PHP binary. An interactive shell may use PHP 8.4 while a scheduled task still resolves PHP 8.2, or may load a different php.ini. That is why recording php -v and php --ini in diagnostic output is useful.

If the project Drush fails, let Composer expose the constraint

Do not install an individual Symfony package globally or delete composer.lock as a first response. Those actions replace evidence with a new, unreviewed dependency set.

Use Composer to test the intended Drush major and show its blockers:

composer prohibits drush/drush ^13 --tree
composer prohibits drush/drush ^14 --tree
composer update drush/drush --with-all-dependencies --dry-run

The dry run previews a solution without writing it. A blocker may be Drupal core, the active PHP version, a contributed package or a project constraint. Resolve that specific relationship instead of forcing installation with --ignore-platform-reqs.

If Drush is missing, add it on a branch or in a controlled build environment. Use a normal requirement when production jobs need Drush; use --dev only when production installations deliberately omit development packages and never call it:

composer require drush/drush:^13
# Or, for a compatible Drupal 11.3+ or Drupal 12 project:
composer require drush/drush:^14

vendor/bin/drush status
composer validate
git diff -- composer.json composer.lock

Choose a currently supported combination

The current Drush compatibility table recommends Drush 13 for Drupal 10.2 and later and for Drupal 11. Drush 14 is recommended for Drupal 11.3 and later and for Drupal 12. Both Drush majors require PHP 8.3 or newer. Drush 12 can still be compatible with Drupal 10, but it is no longer the supported line.

Check Drupal’s PHP matrix as well as Drush’s. In September 2026, Drupal 10.4–10.6 support PHP 8.1 through 8.4; Drupal 11.1–11.4 support PHP 8.3 and 8.4, while PHP 8.5 support begins with Drupal 11.3. Drupal 12 requires PHP 8.5. A PHP release being newer does not automatically make it valid for an older Drupal minor.

Deploy the fix as a controlled change

Commit both composer.json and composer.lock, build from the lock file, and test the commands your release actually uses: status, cache rebuild, database updates, configuration import and any project-specific command files. Run composer check-platform-reqs in the target environment before allowing deployment to continue.

For a legacy site that cannot yet move to a supported combination, pin the last compatible project-local Drush release, isolate its runtime and document the limitation. Treat that as containment with an upgrade owner and date—not as a permanent platform decision.

Make the repair last

  • Keep Drush and its exact resolved version under project control.
  • Use the project binary consistently in local, CI, staging and production workflows.
  • Compare CLI PHP versions and loaded configuration across environments.
  • Test Drupal, Drush and PHP compatibility as one upgrade decision.
  • Remove undocumented global dependencies from operational procedures.

The immediate code change may be small. The valuable outcome is a delivery process that produces the same command-line environment every time. If an inherited Drupal platform has accumulated fragile scripts or unclear dependency ownership, Greg can help turn it into a documented, supportable setup.

Related on GrN.dk

Need help with this kind of work?

Talk to Greg about your Drupal delivery setup Get in touch with Greg.

Sources

Latest articles

Website slowing down during campaigns? Find where requests queue across HTTP/2, NGINX, Apache and PHP-FPM, then test whether a targeted change helps.

When a customer requests access to their personal data, AI can help bring together material from WordPress and Microsoft 365. Here’s how to keep track of data sources, responsibilities and checks.

Put supplier matching, duplicate checks, arithmetic validation, and exception review into your AI invoice workflow before records reach your accounting system.

A small Drupal fix can affect unrelated live settings. Review the full configuration import, resolve environment differences and rehearse deployment and recovery.

An ingredient change may mean a menu needs updating, even when the recipe’s modification date stays the same. Here is how AI can help with review and approval in WordPress.

Check whether AI crawlers can reach your service and product content. Compare response HTML with rendered pages, then identify practical CMS and rendering fixes.

Improve site search for customers who use everyday language. See where synonyms, semantic retrieval and behavioural signals help, while keeping product codes reliable.

Follow customer data through n8n, OpenAI and your CRM. Check who can access retained copies, what redaction hides and whether deletion works before scaling.

When checkout fails, your operations provider needs concrete evidence to work with. See how AI, dmesg and journalctl can gather the evidence into a useful incident ticket.

OpenAI’s hosted Evals platform is closing. Preserve your tests, validate replacement scoring and keep releases covered before the October and November 2026 deadlines.