{"record":{"id":"909db5cf5854ee40","repo":"elsa-workflows/elsa-core","slug":"cannot-transition-from-substatus-to-substatus","errorCode":null,"errorMessage":"Cannot transition from {SubStatus} to {subStatus}","messagePattern":"Cannot transition from (.+?) to (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs","lineNumber":565,"sourceCode":"\n    /// Updates a custom property with the specified key on the <see cref=\"Properties\"/> dictionary.\n    public T UpdateProperty<T>(string key, Func<T?, T> updater)\n    {\n        var value = GetProperty<T?>(key);\n        value = updater(value);\n        Properties[key] = value!;\n        return value;\n    }\n\n    /// Returns true if the <see cref=\"Properties\"/> dictionary contains the specified key.\n    public bool HasProperty(string name) => Properties.ContainsKey(name);\n\n    internal bool CanTransitionTo(WorkflowSubStatus targetSubStatus) => ValidateStatusTransition();\n\n    internal void TransitionTo(WorkflowSubStatus subStatus)\n    {\n        if (!ValidateStatusTransition())\n            throw new($\"Cannot transition from {SubStatus} to {subStatus}\");\n\n        SubStatus = subStatus;\n        UpdatedAt = SystemClock.UtcNow;\n\n        if (Status == WorkflowStatus.Finished)\n            FinishedAt = UpdatedAt;\n\n        if (Status == WorkflowStatus.Finished || SubStatus == WorkflowSubStatus.Suspended)\n        {\n            foreach (var registration in _cancellationRegistrations)\n                registration.Dispose();\n        }\n    }\n\n    /// Creates a new <see cref=\"ActivityExecutionContext\"/> for the specified activity.\n    public async Task<ActivityExecutionContext> CreateActivityExecutionContextAsync(IActivity activity, ActivityInvocationOptions? options = null)\n    {\n        var activityDescriptor = await ActivityRegistryLookup.FindAsync(activity) ?? throw new ActivityNotFoundException(activity.Type);","sourceCodeStart":547,"sourceCodeEnd":583,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs#L547-L583","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nworkflowExecutionContext.TransitionTo(WorkflowSubStatus.Running);\n// after\nif (workflowExecutionContext.CanTransitionTo(WorkflowSubStatus.Running))\n    workflowExecutionContext.TransitionTo(WorkflowSubStatus.Running);","handlingStrategy":"validation","validationCode":"if (!workflowExecutionContext.CanTransitionTo(WorkflowSubStatus.Running))\n    throw new InvalidOperationException($\"Workflow in {workflowExecutionContext.SubStatus} cannot transition to Running\");","typeGuard":"bool canTransition = context.CanTransitionTo(targetSubStatus);","tryCatchPattern":"try { context.TransitionTo(target); } catch (Exception ex) { logger.LogWarning(ex, \"Illegal sub-status transition to {Target} from {Current}\", target, context.SubStatus); }","preventionTips":["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"],"tags":["state-machine","workflow","csharp"],"backgroundTag":"invalid-state-transition","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}