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
- 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'];
}
}
-
Dispatch the workflow
-
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?
Summary
When a workflow batches activities, waits for them with
ActivityStub::all(), and then callsWorkflowStub::continueAsNew(), the activity that triggered the handoff can finish after the workflow is already inWorkflowContinuedStatus. During its middleware cleanup the activity callsStoredWorkflow::active(), which returnsnullbecause 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 returnedThe queue retries the activity and an
ActivityFailedevent is dispatched. This makescontinueAsNewunusable for batch processing patterns.Environment
Reproduction Steps
Dispatch the workflow
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:
Notes / Workarounds
Is
continueAsNewsupposed to be used withyield ActivityStub::all(then we should fix it) or am I trying to do something that is simply not supported?