Skip to content

continueAsNew fails when the last activity in a batch finishes after the workflow is marked continued #260

@therealcljohn

Description

@therealcljohn

Summary

When a workflow batches activities, waits for them with ActivityStub::all(), and then calls WorkflowStub::continueAsNew(), the activity that triggered the handoff can finish after the workflow is already in WorkflowContinuedStatus. During its middleware cleanup the activity calls StoredWorkflow::active(), which returns null because the new “active” child workflow has not yet been attached. PHP then throws:

TypeError: Workflow\Models\StoredWorkflow::active(): Return value must be of type Workflow\Models\StoredWorkflow, null returned

The queue retries the activity and an ActivityFailed event is dispatched. This makes continueAsNew unusable for batch processing patterns.

Environment

  • Laravel Workflow: 1.0.36
  • Laravel: 11.x
  • PHP: 8.2
  • Queue driver: database

Reproduction Steps

  1. Create the following workflow and activity.
// app/Workflows/SampledImportWorkflow.php
namespace App\Workflows;

use Workflow\ActivityStub;
use Workflow\Workflow;
use Workflow\WorkflowStub;

class ImportWorkflow extends Workflow
{
    public function execute(int $maxResults = 20, array $results = [])
    {
        while (count($results) < $maxResults) {
            $activities = [
                ActivityStub::make(ProcessRecordActivity::class),
                ActivityStub::make(ProcessRecordActivity::class),
                ActivityStub::make(ProcessRecordActivity::class),
                ActivityStub::make(ProcessRecordActivity::class),
            ];

            $results = array_merge($results, yield ActivityStub::all($activities));

            return yield WorkflowStub::continueAsNew($maxResults, $results);
        }

        return $results;
    }
}

// app/Activities/ProcessRecordActivity.php
namespace App\Activities;

use Workflow\Activity;

class ProcessRecordActivity extends Activity
{
    public function execute(): array
    {
        return ['status' => 'done'];
    }
}
  1. Dispatch the workflow

  2. Observe the worker logs.

Expected Behavior

The workflow should continue through each chunk, handing off to the next via continueAsNew(), and eventually complete with the aggregated results. No activities should fail.

Actual Behavior

After the first chunk finishes:

  • The last activity result triggers ActivityMiddleware.
  • StoredWorkflow::active() returns null because the workflow has already continued but the new active child isn’t linked.
  • A TypeError is thrown, e.g.:
TypeError: Workflow\Models\StoredWorkflow::active(): Return value must be of type Workflow\Models\StoredWorkflow, null returned in /var/www/html/vendor/laravel-workflow/laravel-workflow/src/Models/StoredWorkflow.php:134
Stack trace:
#0 /var/www/html/vendor/laravel-workflow/laravel-workflow/src/WorkflowStub.php(192): Workflow\Models\StoredWorkflow->active()
#1 /var/www/html/vendor/laravel-workflow/laravel-workflow/src/WorkflowStub.php(187): Workflow\WorkflowStub->status()
#2 /var/www/html/vendor/laravel-workflow/laravel-workflow/src/Workflow.php(120): Workflow\WorkflowStub->running()
#3 /var/www/html/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(36): Workflow\Workflow->handle()
[...]
  • The activity is released and the middleware dispatches ActivityFailed, yet the workflow has already transitioned to the next run.

Notes / Workarounds

  • Running the same batching logic without continueAsNew() (i.e. single workflow instance iterating chunk by chunk) avoids the error because no second workflow is created.
  • The issue stems from StoredWorkflow::active() assuming an active child exists immediately after the status transitions to WorkflowContinuedStatus.

Is continueAsNew supposed to be used with yield ActivityStub::all (then we should fix it) or am I trying to do something that is simply not supported?

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions