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

grayloon's avatar

Weird queue serialization on null arguments

I've been investigating an issue with queue serialization in Laravel and could use some help. Here's the scenario:

In my console scheduler, I have:

// Console.php
Schedule::job(MyJobClass::class)->daily();

Now, in my job:

// MyJobClass.php
public function __construct(
    public ?Carbon $date = null,
) {
    $this->date ??= today()->subDay();

    // Why is $date a new instance of Carbon of today, instead of today()->subDay()?
}

When the job is scheduled via the console, $date is a new Carbon instance of today, not today()->subDay(). However, if I fire the job manually (sync or non-console with no arguments), the expected today()->subDay() is passed.

It seems something in the job serialization process during scheduling is instantiating a new Carbon object. I moved the logic to the handle() method as a workaround, but I'm curious—what quirks in queue serialization might be causing this? Any ideas?

Thanks!

0 likes
1 reply
LaryAI's avatar
Level 58

The issue you're encountering is related to how Laravel handles job serialization and deserialization. When a job is serialized, the constructor is not called again during deserialization. Instead, the properties are directly restored from the serialized data. This means that any default values or logic in the constructor won't be re-applied when the job is deserialized from the queue.

In your case, when the job is serialized, the date property is set to null (since that's the default value), and this null value is what's stored. When the job is deserialized, the constructor is not called again, so the logic to set $this->date to today()->subDay() is not executed.

To handle this, you can move the logic to set the default value into the handle method or another method that is called after deserialization. Here's how you can adjust your job class:

// MyJobClass.php
public function __construct(
    public ?Carbon $date = null,
) {
    // Constructor logic is not reliable for setting defaults when using queues
}

public function handle()
{
    $this->date ??= today()->subDay();

    // Your job logic here
}

By moving the default value logic to the handle method, you ensure that it is executed every time the job is processed, regardless of whether it was serialized and deserialized. This approach ensures that your default logic is applied consistently.

Please or to participate in this conversation.