DNABeast's avatar

Concurrency fails in Livewire with an unserialize error

I've set up a livewire component and when I pass a variable into a Concurrency call it fails. unserialize(): Error at offset 157 of 387 bytes As I understand it the function is serialized first before being processed.

I suspect this is to do with the limitations of Livewire and how it passes data around interfering with how the concurrency serialization is done. Is anyone able to shed a little light? In the code below replacing the reference to $this->newPiece with a string fixes the problem but removes the ability to make the value dynamic.

class ConcurrentTest extends Component
{
	public $newPiece = 'King';
	public $pieces = ['pawn','bish'];

	public function render()
	{
		return view('livewire.concurrent-test');
	}

	public function mount()
	{
		[$this->pieces[], $this->pieces[]] = Concurrency::run([
			fn() =>  'Draught',
			fn() =>  $this->newPiece
		]);
	}
}

I'm thinking I might have to make my concurrent call a Http post to a separate endpoint but I'd rather avoid that if there's a better solution.

0 likes
2 replies
LaryAI's avatar
LaryAI
AI
Best Answer
Level 58

The issue you’re encountering is due to how PHP closures (anonymous functions) are serialized when using concurrency helpers (like Laravel’s Bus::batch, Laravel Octane, or similar concurrency libraries). In your code, the closure fn() => $this->newPiece captures the $this context (the Livewire component instance), which is not serializable by default. Livewire serializes the component state between requests, and when it tries to serialize the closure (which references $this), it fails, resulting in the unserialize(): Error at offset... error.

Why does replacing $this->newPiece with a string work?
Because the closure no longer captures the $this context, making it serializable.

Solution

Extract the dynamic value before passing it into the closure.
Assign the value you want to use to a local variable, and then use that variable inside the closure. This way, the closure does not capture the Livewire component instance.

Here’s how you can modify your code:

class ConcurrentTest extends Component
{
    public $newPiece = 'King';
    public $pieces = ['pawn','bish'];

    public function render()
    {
        return view('livewire.concurrent-test');
    }

    public function mount()
    {
        $newPiece = $this->newPiece; // Extract the value to a local variable

        [$this->pieces[], $this->pieces[]] = Concurrency::run([
            fn() => 'Draught',
            fn() => $newPiece // Use the local variable, not $this
        ]);
    }
}

Key Point:
Avoid referencing $this inside closures that will be serialized for concurrency. Always extract the needed data into local variables first.


If you need to update $this->pieces dynamically based on the result, do so after the concurrency call:

public function mount()
{
    $newPiece = $this->newPiece;

    $results = Concurrency::run([
        fn() => 'Draught',
        fn() => $newPiece
    ]);

    $this->pieces[] = $results[0];
    $this->pieces[] = $results[1];
}

Summary:

  • Don’t reference $this inside closures passed to concurrency helpers.
  • Extract any needed data into local variables first.
  • Use those variables inside your closures.

This should resolve the serialization error you’re seeing!

DNABeast's avatar

Larry actually answered my question. Well done Larry.

Please or to participate in this conversation.