Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

uminnkhantnaing's avatar

Handle Error Of Notification Queue if It fails | Logging instead of Throwing The Error And Stopped The Rest Codes Execution

Hello, I am now working on a project that needs to send the user a booking confirmation email once the payment is completed. I have already set that up by the following steps and working fine except when there is an error.

  • Once the booking is confirmed, there is an observer that sends an email notification (it has ShouldQueue implemented) to the customer on the created or updated method if the payment_status is completed.
if ($booking->isDirty('payment_status') && $booking->payment_status === 'completed') {
   $booking->customer->notify(new PaymentCompletedNoti($booking));
}
  • The toMail() method on PaymentCompletedNoti class sends the email as expected until the provided email address is invalid.
public function toMail($notifiable)
{
   return (new MailMessage)
      ->subject('Payment Completed')
      ->view('emails.booking.payment_completed', ['booking' => $this->booking]);
}
  • On the created or updated method of the controller, I have written other codes that execute after creating or updating the Modal. If the notification has failed due to invalid email address in the notification class, it throws an error and stopped running. I tried adding try - catch block when notifying the customer but it only works without ShouldQueue. Also attempted with failed() method on the notification class as follow but not working.
public function failed($notifiable, $exception)
{
   Log::error('Failed to send notification: ' . $exception->getMessage());
}

In my case, I want the notification class to only log the error using Log facade instead of throwing the fatal error that stops other codes execution. Please let me know how I could do that.

0 likes
7 replies
tisuchi's avatar

@uminnkhantnaing Can you use try-catch block?

if ($booking->isDirty('payment_status') && $booking->payment_status === 'completed') {
    try {
        $booking->customer->notify(new PaymentCompletedNoti($booking));
    } catch (\Exception $e) {
        Log::error('Failed to send notification: ' . $e->getMessage());
    }
}
1 like
uminnkhantnaing's avatar

@tisuchi I have tried that as well. But try-catch block only works if you immediately sends the notification. In my case, it's different since they are queued. I already came up with a solution with the help of ChatGPT. I will comment a new one and mark that as solved soon.

uminnkhantnaing's avatar
Level 1

I was able to make it work by adding this handle method in the Notification class. This way is not that nice since the handle method should be added on every Notification class.

It would be better if it works with try-catch block which we could implement it once while notifying the user. I tested this (try-catch) but not working at that time maybe because of the cache or that literally does not support the queues. Everyone having the same issue could try what @tisuchi commented as well. Just FYI.

Handle method inside Notification class

public function handle($notifiable, Throwable $exception)
{
    Log::error('Failed to send notification: ' . $exception->getMessage());
}
1 like
musciplay's avatar

@uminnkhantnaing - Adding the handle method is unfortunately not working for me. The exception is getting thrown as usual, and anything I add to my handle method isn't being run.

I can access exceptions thrown to the "failed" method, but can't prevent them from being thrown to the error log.

Using Laravel 11 and Mailhog for testing.

musciplay's avatar

Following up, found a cleaner solution.

If there is a specific error you are trying to avoid, you can configure that in the global exception handler. Since I upgraded from Laravel 10 -> 11, I'm using Laravel 10's exception structure. So added protected $dontReport to App\Exceptions\Handler.php.

For Laravel 11, syntax would be here. https://laravel.com/docs/11.x/errors#ignoring-exceptions-by-type

protected $dontReport = [
        InvalidEmailException::class,
];

This method still shows the exception in the "failed" method on the notification still allows me catch the exception and do something with it. Just won't throw it to the log channels.

    public function failed(Throwable $e) {
        if ($e instanceof PostmarkTransportException) {
            // do something with it
        }
    }
1 like
xsjk2023's avatar

Found a flexible way to allow all channels to attempt sending, even if one fails. This approach also lets you handle specific errors (e.g., clearing an FCM token upon failure). The core idea is to override the Notification class to set a temporary channel and create a custom NotificationService that iterates through each channel by setting the temporary channel .

class NotificationService
{
    public function notify(object $notifiable, BaseNotification $notification)
    {
        $listOfChannels = $notification->via($notifiable);
        foreach ($listOfChannels as $channel) {
            $notification->setCurrentChannel($channel);
            try {
                $notifiable->notify($notification);
            } catch (CouldNotSendNotification $e) {
                // TODO: specific error handling (e.g., clear FCM token)
            } catch (\Exception $e) {
                // TODO: general error handling
            }
        }
        $notification->clearCurrentChannel();
    }
}
class BaseNotification extends Notification
{
    public string $currentChannel;

    public function via($notifiable)
    {
        return $this->currentChannel ? [$this->currentChannel] : ['database', 'mail', FcmChannel::class];
    }

    public function setCurrentChannel(string $channel): void
    {
        $this->currentChannel = $channel;
    }

    public function getCurrentChannel(): string
    {
        return $this->currentChannel;
    }

    public function clearCurrentChannel(): void
    {
        $this->currentChannel = '';
    }
}
class MyProgramOrJob {
    public function run()
    {
        // $notifiable  
        // $notification - you custom notificaiton that extend BaseNotification

        (new NotificationService)->notify(
            $notifiable, 
            $notification,
        );
    }
}

Please or to participate in this conversation.