elsa-workflows / elsa-workflows/elsa-core
Workflow state not persisted after TriggerSignalAsync in Elsa 2.15.2 (breaking change from 2.14.1)
- Dominant language
- C#
- Stars
- 7.9k
- Forks
- 1.5k
- Avg merge
- 15h 22m
- Merged PRs (30d)
- 114
Description
## Description
After upgrading from Elsa 2.14.1 to 2.15.2, workflows using ISignaler.TriggerSignalAsync no longer persist state changes to the database, despite executing successfully. The workflow executes (confirmed by ExecutionLog table having records), but the WorkflowInstances table is not updated.
**Breaking Change**: In Elsa 2.14.1, both IWorkflowInstanceExecutor.ExecuteAsync and ISignaler.TriggerSignalAsync automatically persisted workflow state. In 2.15.2, neither method auto-persists, requiring manual persistence code.
**The Problem**: The persistence pattern that successfully fixes ExecuteAsync does NOT work for TriggerSignalAsync because:
- ExecuteAsync returns RunWorkflowResult with populated WorkflowInstance ✅
- TriggerSignalAsync returns IEnumerable where WorkflowInstance is always null ❌
This makes it impossible to apply the same persistence pattern to both methods, and there is no migration guide or documentation explaining how to persist state after TriggerSignalAsync in 2.15.2.
Error: No exception is thrown. Workflow executes silently but state changes are lost.
## Steps to Reproduce
**Detailed Steps**
1. Upgrade project from Elsa 2.14.1 to 2.15.2
2. Upgrade from .NET 8 to .NET 9
3. Create a workflow with SignalReceived blocking activity
4. Store and start the workflow (workflow suspends at SignalReceived)
5. Call TriggerSignalAsync to resume the workflow
6. Check database: ExecutionLog has records (proves execution) but WorkflowInstances table is not updated
**Code Snippets**
Original Code (Worked in Elsa 2.14.1):
```
// Both methods auto-persisted in 2.14.1
public async Task StartWorkflow(string correlationId)
{
var workflowInstance = await _workflowInstanceStore.FindAsync(
new CorrelationIdSpecification(correlationId));
var input = new Variables();
input.Set("outcome", "Done");
if (workflowInstance != null)
{
// ✅ Worked in 2.14.1 - auto-persisted
await _workflowInstanceExecutor.ExecuteAsync(
workflowInstance, null, new WorkflowInput(input));
}
}
public async Task ResumeWorkflow(string correlationId)
{
var workflowInstance = await _workflowInstanceStore.FindAsync(
new CorrelationIdSpecification(correlationId));
var input = new Variables();
input.Set("outcome", "Done");
if (workflowInstance != null)
{
// ✅ Worked in 2.14.1 - auto-persisted
await _signaler.TriggerSignalAsync("Signal",
workflowInstanceId: workflowInstance.Id,
correlationId: correlationId,
input: input);
}
}
```
After Upgrading to 2.15.2 (Both Methods Stopped Persisting):
Both methods execute successfully but don't persist workflow state changes to the database.
Fix That WORKS for ExecuteAsync in 2.15.2:
```
public async Task StartWorkflow(string correlationId)
{
var workflowInstance = await _workflowInstanceStore.FindAsync(
new CorrelationIdSpecification(correlationId));
var input = new Variables();
input.Set("outcome", "Done");
if (workflowInstance != null)
{
// Execute the workflow
var result = await _workflowInstanceExecutor.ExecuteAsync(
workflowInstance, null, new WorkflowInput(input));
// ✅ FIX: Manually persist the updated instance
if (result?.WorkflowInstance != null)
{
await _workflowInstanceStore.UpdateAsync(result.WorkflowInstance);
}
}
// ✅ NOW WORKS in 2.15.2
}
```
Same Fix Attempted for TriggerSignalAsync (DOES NOT WORK):
```
public async Task ResumeWorkflow(string correlationId)
{
var workflowInstance = await _workflowInstanceStore.FindAsync(
new CorrelationIdSpecification(correlationId));
var input = new Variables();
input.Set("outcome", "Done");
if (workflowInstance != null)
{
// Execute the workflow
var results = await _signaler.TriggerSignalAsync("Signal",
workflowInstanceId: workflowInstance.Id,
correlationId: correlationId,
input: input);
// ❌ PROBLEM: results[].WorkflowInstance is ALWAYS NULL
foreach (var result in results)
{
if (result?.WorkflowInstance != null) // This is ALWAYS false
{
await _workflowInstanceStore.UpdateAsync(result.WorkflowInstance);
}
}
}
// ❌ STILL DOES NOT PERSIST in 2.15.2
}
```
**Workflow Configuration:**
• Uses SignalReceived activity with signal name "Signal"
• Custom activities inherit from SignalReceived base class
• Activities implement OnExecuteAsync and OnResumeAsync methods
• Uses correlation IDs for workflow instance tracking
**Reproduction Rate**
Every time (100% reproducible after upgrade)
**Video/Screenshots**
N/A - Issue is database-level state persistence, not UI-related
**Additional Configuration**
• Using Entity Framework Core persistence (Elsa.Persistence.EntityFramework.SqlServer)
• SQL Server database
• Clean Architecture project structure
• Workflows use IWorkflowInstanceStore for persistence
• Custom activities save business data separately (this works correctly)
## Expected Behavior
**Option 1 (Ideal)**: Both ExecuteAsync and TriggerSignalAsync should auto-persist workflow state like they did in 2.14.1.
**Option 2 (Acceptable)**: If manual persistence is required in 2.15.2, both methods should return the updated WorkflowInstance so the same persistence pattern can be applied:
```
// This pattern should work for BOTH methods
var result = await _someExecutionMethod(...);
if (result?.WorkflowInstance != null)
await _workflowInstanceStore.UpdateAsync(result.WorkflowInstance);
```
**Option 3 (Minimum)**: Provide clear documentation/migration guide showing the correct persistence pattern for TriggerSignalAsync in 2.15.2.
## Actual Behavior
**Specific Fields NOT Updated**:
• LastExecuted - Remains null
• ActivityData - Not populated with new activities
• BlockingActivities - Not updated with new blocking activities
• WorkflowStatus - Remains in old state (e.g., Idle instead of Suspended)
## Screenshots
If possible, add screenshots or screen recordings to help explain the problem.
## Environment
• Elsa Package Version: 2.15.2 (upgraded from 2.14.1 where it worked)
• .NET Version: .NET 9 - upgraded from .NET 8
• Elsa Persistence Package: Elsa.Persistence.EntityFramework.SqlServer 2.15.2
• Operating System: Windows 10/11
• Database: Microsoft SQL Server
• Project Type: ASP.NET Core Clean Architecture
## Log Output
No errors or exceptions are logged. The workflow executes silently but state changes are not persisted.
## Troubleshooting Attempts
✅ Successfully Fixed ExecuteAsync:
```
var result = await _workflowInstanceExecutor.ExecuteAsync(...);
if (result?.WorkflowInstance != null)
await _workflowInstanceStore.UpdateAsync(result.WorkflowInstance);
```
❌ Failed Attempts to Fix TriggerSignalAsync:
**Attempt 1**: Direct application of ExecuteAsync fix pattern
```
var results = await _signaler.TriggerSignalAsync(...);
foreach (var result in results)
{
if (result?.WorkflowInstance != null) // Always false - instance is null
await _workflowInstanceStore.UpdateAsync(result.WorkflowInstance);
}
```
Result: result.WorkflowInstance is always null, so UpdateAsync never executes
**Attempt 2**: Reload workflow instance after trigger
```
await _signaler.TriggerSignalAsync(...);
var updated = await _workflowInstanceStore.FindAsync(...); // Gets old/stale data
await _workflowInstanceStore.UpdateAsync(updated); // Saves stale data
```
Result: Reloaded instance has old data, so saving it has no effect
**Attempt 3**: Using SaveAsync instead of UpdateAsync
```
await _signaler.TriggerSignalAsync(...);
var updated = await _workflowInstanceStore.FindAsync(...);
await _workflowInstanceStore.SaveAsync(updated);
```
Result: No effect - still persists old data
**Attempt 4**: Manual DbContext access
```
await _signaler.TriggerSignalAsync(...);
await using var elsaContext = _elsaContextFactory.CreateDbContext();
await elsaContext.SaveChangesAsync();
```
**Attempt 5**: Adding delay before reload
```
await _signaler.TriggerSignalAsync(...);
await Task.Delay(100); // Wait for async completion
var updated = await _workflowInstanceStore.FindAsync(...);
await _workflowInstanceStore.UpdateAsync(updated);
```
Result: No effect - data is still stale
Contributor guide
Research direction
Start by tracing ISignaler.TriggerSignalAsync, CollectedWorkflow, and IWorkflowInstanceStore alongside IWorkflowInstanceExecutor.ExecuteAsync and RunWorkflowResult in Elsa 2.15.2 with the Entity Framework SQL Server persistence package. Reproduce the signal path and compare the returned instance and persisted WorkflowInstances fields; done means the updated state is persisted or the required migration pattern is documented.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, sql
- Domain
- backend, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100