3

I try Symfony 4.3.0-dev version to get some new features from Messenger component. My command bus works in sync mode.

Before upgrading I could easily throw my custom exception ConflictException from handler. But for 4.3.0-dev I get a Symfony\Component\Messenger\Exception\HandlerFailedException.

How can I catch my custom exception again?

yivi
  • 42,438
  • 18
  • 116
  • 138
akor
  • 175
  • 1
  • 3
  • 13
  • Could you please show some code. I am having a hard time figuring out why this problem happens. Maybe you can also show the full stack trace to see if maybe the HandlerFailedException is thrown earlier and that is why your exception is not called – dbrumann Apr 07 '19 at 13:52

2 Answers2

3

Starting with Symfony 4.3, if a handler throws any exception, it will be wrapped in a Symfony\Component\Messenger\Exception\HandlerFailedException.

This is reflected here in the changelog:

[BC BREAK] A HandlerFailedException exception will be thrown if one or more handler fails.

In places where you are dealing with a synchronous transport and you want to deal with the original exception, you can do something similar to what Api-Platform does in this DispatchTrait:

namespace App\Infrastructure\Messenger;

use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Exception\HandlerFailedException;
use Symfony\Component\Messenger\MessageBusInterface;
use Throwable;

trait DispatchTrait
{

    private ?MessageBusInterface $messageBus;

    /**
     * @param object|Envelope $message
     * @return Envelope
     * @throws Throwable
     */
    private function dispatch($message): ?Envelope
    {
        try {
            return $this->messageBus->dispatch($message);
        } catch (HandlerFailedException $e) {
            while ($e instanceof HandlerFailedException) {
                /** @var Throwable $e */
                $e = $e->getPrevious();
            }

            throw $e;
        }
    }
}

(This version has no backwards compatibility, and does away with the MessageBus check, since I'm only using for internal applications I'm in control of).

In whatever class you are dispatching your message, you could do:

class FooController
{
    use DispatchTrait;

    public function __construct(MessageBusInterface $messageBus) {
        $this->messageBus = $messageBus;
    }

    public function __invoke(Request $request)
    {
        // however you create your message
        $command = Command::fromHttpRequest(); 

        try {
                $this->dispatch($command);
        }
        catch (ConflictException $e) {
            // deal with the original exception
        }
    }
}
yivi
  • 42,438
  • 18
  • 116
  • 138
2

As you can see on CHANGELOG, a BC break was introduced in version 4.3. In my application I was catching exceptions, and I solved by adding following code:

if ($exception instanceof HandlerFailedException) {
    $exception = $exception->getPrevious();
}
Massimiliano Arione
  • 2,422
  • 19
  • 40