> ## Content Index
> Fetch the complete content index at: https://madewithlove.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# When Symfony event listeners go wild!
- URL: https://madewithlove.com/blog/when-symfony-event-listeners-go-wild/
- Published: 2025-02-04T07:46:38.000Z
- Updated: 2025-02-04T08:15:53.000Z
- Description: A simple bug fix can turn into a full-blown debugging adventure - especially when Symfony’s event listeners start misbehaving. 🧐 If your listeners are firing twice (or more), your service configuration might be the culprit. Here’s how we solved a sneaky autoconfiguration.
- Author: Charlotte Emma Grace
- Tags: Engineering, Infrastructure, Processes, Tutorials

It's 3 p.m. on a Wednesday, and you’re sipping your third coffee of the day when you notice what seems like a small bug to fix in the code. *“This will be fine,”* you tell yourself, finishing your coffee with confidence.

Every coder has these glorious moments of chaos and confusion, especially when something small spirals into a time-consuming rabbit hole of debugging despair. For me, there was one such occasion where what started as an *"Oh, that’s pretty weird"* moment quickly turned into a full-scale exploration of Symfony's inner workings.

In this post, I’ll take you along on my journey of debugging some unexpected behaviour with event listeners and how a slight misconfiguration in the way you define these as services could be the culprit.

There's no one-size-fits-all solution when it comes to debugging (*wouldn’t that be nice?*), but the lessons and problem-solving approaches shared here might give you some useful tools for your own debugging tool belt.

### Let’s go!  

### 🌞 In the beginning...  

There was an innocent `profile.linked` event. 

An event subscriber, `SendEmailWhenProfileLinkedEventSubscriber::onProfileLinked`, was behaving a little *too* enthusiastically, delivering the same email multiple times. 

Something was clearly wrong! But what? And why?

It was time to grab my trusty rubber duck (we refer to him as *Professor Quacks*, and he has a **PhDuck.** in Debugging 🦆) and get to work.

Like any good detective, we started gathering evidence. The first step? Seeing how Symfony's event dispatcher was managing the listeners.

We ran the following command to list all registered event listeners in the service container:

```bash
php bin/console debug:event-dispatcher

```

Here’s a small snippet from the output (truncated for brevity):

```bash
"profile.linked" event

Custom\AuthBundle\Listener\SendEmailWhenProfileLinkedEventSubscriber::onProfileLinked()
Custom\AuthBundle\Listener\SendEmailWhenProfileLinkedEventSubscriber::onProfileLinked()
```

**Hold up - why was the listener registered twice?!**

Further investigation revealed this wasn't just limited to the `profile.linked` event. Several other listeners were also registered multiple times, pointing to a broader misconfiguration issue within our service setup.

Professor Quacks and I exchanged knowing looks. **Aha*!* That explained the mystery around the duplicate emails. Now, we just needed to figure out what was causing it.

### 🕵️‍♀️ Following the Trail  

After some more digging, we identified the problem: **Symfony's service tagging and autoconfiguration.**

***Tags*** in Symfony are an interesting concept. They're essentially metadata that tell Symfony how to handle different services. Think of them as labels that mark services for special treatment. For example, when you tag a service as an event listener, you’re telling Symfony:

*"Hey, this service needs to listen for specific events!"*

Symfony’s **autoconfigure** feature provides built-in rules to wire services automatically. If you name a class or use certain interfaces that Symfony expects, it knows how to tag and use these services without extra manual configuration.

In our case, we had enabled `autoconfigure()` globally:

```php
$services
  ->defaults()
  ->autoconfigure()
  ->autowire();

```

At the same time, we were **also manually adding a custom tag** to the service definition:

```php
$services->set(SendEmailWhenProfileLinkedEventSubscriber::class)
  ->args([
      service('email_verification_manager'),
      service('translator'),
  ])
  // Custom tag
  ->tag('kernel.event_listener', [
      'event' => 'profile.linked',
      'method' => 'onProfileLinked',
  ]);
```

This was causing two separate issues:

1. Since we had globally enabled autoconfiguration, **Symfony was already tagging our event listeners** based on their class names and interfaces.
2. We had **also manually defined custom tags** on these listeners.

This double-tagging meant that Symfony was registering the same listener twice, once from the autoconfiguration and once from our custom tags.

### 🔥 Bringing Order to the Chaos  

We needed to be more intentional about how services were configured to prevent this duplication.

**The Fix: Disabling autoconfiguration for duplicated isteners**

By disabling autoconfiguration, we ensured Symfony wouldn't automatically tag services that we were already tagging manually.

**Before (Problematic Code):**

```php
$services
  ->set('SendEmailWhenProfileLinkedEventSubscriber::class)
  ->args([
      service('email_verification_manager'),
      service('translator')
  ])
  ->tag('kernel.event_listener', [
      'event' => 'profile.linked',
      'method' => 'onProfileLinked',
  ]);
```

**After (Fixed Code):**

```php
$services
  ->set('SendEmailWhenProfileLinkedEventSubscriber::class)
  ->args([
      service('email_verification_manager'),
      service('translator')
  ])
  ->tag('kernel.event_listener', [
      'event' => 'profile.linked',
      'method' => 'onProfileLinked',
  ])
  ->autoconfigure(false);
```

Disable autoconfiguration for this service

The configuration above explicitly tags the event listener just **once** and avoids any accidental re-tagging that **autoconfiguration** might have introduced by setting `autoconfigure` to `false`. It also allows for more precise control over which events the listener should be registered for.

Alternatively, we could have removed the custom tag entirely from the service definition (then we wouldn't have needed to set `autoconfigure` to `false`) and it would have had the same effect, but we opted for explicit configuration. **Why?** Because when debugging is involved, explicit is often **better than implicit**.

We also **scanned our configuration files** to check for any accidental duplicate imports - better safe than sorry!

### 💡 Moment of Truth  

We ran the `debug:event-dispatcher` command again and...

🚨 **STOP THE BUS!** 🚨

It worked!

The listener now appeared **only once** in the list:

```bash
"profile.linked" event

Custom\AuthBundle\Listener\SendEmailWhenProfileLinkedEventSubscriber::onProfileLinked()
```

​​Output from debug:event-dispatcher command

### 🧑‍🎓 What Did We Learn?  

🎯 **Autoconfiguration is powerful but needs care.** It can save a lot of time, but you have to be cautious about it. **It's important to understand how and when services are being tagged to avoid unintended side effects.**

🔍 **Symfony has some pretty awesome debug tools!** The `debug:event-dispatcher` command was a lifesaver here and really helped get some visibility on what was happening under the hood.

📌 **Be explicit when necessary.** In many cases, being explicit in the service definitions can prevent headaches and while **autoconfiguration** is convenient, sometimes a little manual configuration goes a long way and is worth the clarity that it brings.

### 🌈 The Happy Ending  

After all the quacks, tweaks and fixes, the event listener **finally** learned to send emails just once. Our users stopped getting spammed, and all was well in the land of `profile.linked` events.

Remember, fellow developers: **never underestimate the importance of a good rubber duck!** (*Professor Quacks made me say that.*) 🦆