elsa-workflows/elsa-core · error · Exception
Cannot transition from
Error message
Cannot transition from {SubStatus} to {subStatus} What it means
WorkflowExecutionContext enforces a legal lifecycle for workflow sub-statuses. TransitionTo validates that the move from the current WorkflowSubStatus to the target is allowed; if not, this exception is thrown and the status is not changed. It guards the internal state machine against illegal or re-entrant status updates.
Solutions
- Inspect the workflow's current SubStatus before calling TransitionTo and only issue legal transitions (e.g. guard with workflowExecutionContext.CanTransitionTo(target)).
- Fix activity/host logic that schedules work or resumes bookmarks after the workflow reached a terminal status (Finished/Cancelled/Faulted).
- Serialize status-changing operations (use a single execution pipeline or locking) to prevent concurrent completions racing.
- Check whether a bookmark resume is stale (workflow instance already completed) before resuming, e.g. re-load instance state and skip if Finished.
- Verify the transition matrix matches your Elsa version; if using custom statuses, align with the supported transitions in ValidateStatusTransition.
Example fix
// before
workflowExecutionContext.TransitionTo(WorkflowSubStatus.Running);
// after
if (workflowExecutionContext.CanTransitionTo(WorkflowSubStatus.Running))
workflowExecutionContext.TransitionTo(WorkflowSubStatus.Running); Defensive patterns
Strategy: validation
Validate before calling
if (!workflowExecutionContext.CanTransitionTo(WorkflowSubStatus.Running))
throw new InvalidOperationException($"Workflow in {workflowExecutionContext.SubStatus} cannot transition to Running"); Type guard
bool canTransition = context.CanTransitionTo(targetSubStatus);
Try / catch
try { context.TransitionTo(target); } catch (Exception ex) { logger.LogWarning(ex, "Illegal sub-status transition to {Target} from {Current}", target, context.SubStatus); } Prevention
- Check CanTransitionTo before every manual TransitionTo call
- Never resume bookmarks/instances already in a terminal status
- Avoid manipulating workflow status from multiple threads without synchronization
When it happens
Trigger: Calling WorkflowExecutionContext.TransitionTo with a target sub-status that is not a permitted successor of the current SubStatus (e.g. attempting to move a Finished workflow back to Running, or double-completing an already-Cancelled workflow), typically from custom host code or an activity that manipulates workflow status directly.
Common situations: Custom runners or middleware that resume a completed workflow; race conditions where two threads complete the same workflow instance; hosts upgrading Elsa versions where the transition matrix changed; manually resuming a bookmark after the workflow already finished.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Activity descriptor not found
- Activity not found.
- Activity type not found
- is required.
- No input with name could be found
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/909db5cf5854ee40.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs:565
/// Updates a custom property with the specified key on the <see cref="Properties"/> dictionary.
public T UpdateProperty<T>(string key, Func<T?, T> updater)
{
var value = GetProperty<T?>(key);
value = updater(value);
Properties[key] = value!;
return value;
}
/// Returns true if the <see cref="Properties"/> dictionary contains the specified key.
public bool HasProperty(string name) => Properties.ContainsKey(name);
internal bool CanTransitionTo(WorkflowSubStatus targetSubStatus) => ValidateStatusTransition();
internal void TransitionTo(WorkflowSubStatus subStatus)
{
if (!ValidateStatusTransition())
throw new($"Cannot transition from {SubStatus} to {subStatus}");
SubStatus = subStatus;
UpdatedAt = SystemClock.UtcNow;
if (Status == WorkflowStatus.Finished)
FinishedAt = UpdatedAt;
if (Status == WorkflowStatus.Finished || SubStatus == WorkflowSubStatus.Suspended)
{
foreach (var registration in _cancellationRegistrations)
registration.Dispose();
}
}
/// Creates a new <see cref="ActivityExecutionContext"/> for the specified activity.
public async Task<ActivityExecutionContext> CreateActivityExecutionContextAsync(IActivity activity, ActivityInvocationOptions? options = null)
{
var activityDescriptor = await ActivityRegistryLookup.FindAsync(activity) ?? throw new ActivityNotFoundException(activity.Type);View on GitHub (pinned to fe9217bdfa)