Framework: Listening on Illuminate\Mail\Events\MessageSent causes Error code: 5 when email has attachments

Created on 14 Jun 2019  路  14Comments  路  Source: laravel/framework

  • Laravel Version: 5.8.22
  • PHP Version: 7.2.3
  • Database Driver & Version: MySQL 5.7

Description:

We are attempting to run a listener for the Illuminate\Mail\Events\MessageSent, this causes issues when the email being sent has attachments. This is happening while using the redis queue but i believe will happen when database queue is used as well.

The error returned is: Unable to JSON encode payload. Error code: 5

Steps To Reproduce:

Create a listener that listens on Illuminate\Mail\Events\MessageSent, send an email with an attachment, when the queue picks up the job it'll throw the error mentioned above.

I believe this is due to the fact that the event exposes the entire Swift Message instance with the attachments raw data. I am not sure what the best solution is here but i thought i'd report it to see if there is anything that can be done or a workaround (i've searched and found nothing so far).

Thank You.

bug

Most helpful comment

All 14 comments

Error code: 5

This error is related to invalid unicode byte sequences in the payload and may be specific to the data you try to send.

Please debug the output of $this->createPayloadArray($job, $queue, $data), it seems it's not valid for encoding via json.

@mfn Yeah, I understand the error. It's due to a PDF attachment. if we have no attachments or for example attach a simple txt file it does not happen.

I see. It's possible to create payload hooks using \Illuminate\Queue\Queue::createPayloadUsing.

The way I understand it, the callback receives your data and you can transform the payload before it's returned for serialization.

@mfn Is this the preferred method of doing this? We have to make the data safe prior to saving it in storage?

@VinceG I'm afraid that's the only way to do it yeah. I have no idea if this could be considered an actual bug or not. I'll leave this open with a "needs more info" label for now to check into it at a later time.

@driesvints Thank You. I'll give this a shot to see if that'll work.

I can confirm this issue as well. I ran into this lately, while not experiencing this when I was working on the project (late 2018).

I had to remove the ShouldQueue contract to get things working again.

It is related to the payload as mentioned, but didn't have the time to dive into this.

As suggested by @mfn you could create a payload hook in the register method of your AppServiceProvider.

I was able to get around this issue with the following code:

$this->app['events']->listen(\Illuminate\Mail\Events\MessageSent::class, function ($event) {
    \Illuminate\Queue\Queue::createPayloadUsing(function ($connection, $queue, $payload) {
        $message = $payload['data']['command']->data[0]->message;

        // remove the attachments from Swift_Message
        collect($message->getChildren())->each(function ($item) use ($message) {
            $message->detach($item);
        });

        // the data property contains the Swift_Message as well,
        // therfore you have to remove it
        unset($payload['data']['command']->data[0]->data['message']);

        return $payload;
    });
});

1) The MessageSent event has a $message and a $data attribute. $message is an instance of Swift_Message and $data is an array which also contains the Swift_Message, which is actually an instance of Illuminate\Mail\Message that contains that same Swift_Message object.
This is due to this line of code.
We have some duplication here, and may not be necessary. We might consider to get rid of this duplication before we fire the MessageSent event.
2) We are firing an MessageSent event where we can listen to as described in the docs. If you are queueing that listener you may get in the situation where the payload can not be JSON encoded due to the fact that the Swift_Message contains attachments with binary data. Because we can queue event listeners I think we should sanitize the data somehow before firing the MessageSent event.

@mvdnbrk Thank You! I am going to give this a try.

@mvdnbrk @driesvints That worked perfectly. I think this should be a fix applied to the framework, but instead of removing the attachments it should just encode them (base64 encode or similar) to make them serialize safe.

Also getting this same error due to using the MessageSending or MessageSent event. Also with a PDF attachment.

@nickescobedo The above code actually worked. It still needs to be properly addressed by the framework but in the meantime the solution proposed above will work.

@VinceG It worked for me as well, but still had the issue.

Was this page helpful?
0 / 5 - 0 ratings