elsa-workflows/elsa-core · error · InvalidOperationException
StateMachine transitions cannot share a Trigger activity in…
Error message
StateMachine transitions cannot share a Trigger activity in Elsa 3.8. Give each transition its own trigger activity with a unique ID.
What it means
StateMachine.EnsureSupportedTriggerIdentities throws InvalidOperationException when two or more transitions reference the same trigger activity instance or two triggers share the same non-empty Id. Elsa 3.8 requires each transition to have its own uniquely-identified trigger so trigger/bookmark bookkeeping stays unambiguous. Validated during ExecuteAsync.
Solutions
- Give each transition its own trigger activity instance with a unique Id.
- If multiple transitions should react to the same event, create separate trigger instances (one per transition).
- De-duplicate trigger Ids across transitions in the state machine definition.
- When building state machines in code, construct a new trigger inside the loop instead of reusing a captured variable.
Example fix
// before
var trigger = new EventTrigger("OrderReceived") { Id = "t1" };
transitions.Add(new Transition { Trigger = trigger });
transitions.Add(new Transition { Trigger = trigger }); // shared instance -> throws
// after
transitions.Add(new Transition { Trigger = new EventTrigger("OrderReceived") { Id = "t1" } });
transitions.Add(new Transition { Trigger = new EventTrigger("OrderReceived") { Id = "t2" } }); Defensive patterns
Strategy: validation
Validate before calling
// before execution, check trigger uniqueness across transitions
var triggerIds = stateMachine.Transitions.Select(t => t.Trigger.Id).Where(id => !string.IsNullOrWhiteSpace(id)).ToList();
if (triggerIds.Count != triggerIds.Distinct().Count())
throw new InvalidOperationException("State machine transitions must use unique trigger Ids."); Type guard
static bool HasUniqueTriggers(StateMachine sm)
{
var seen = new HashSet<object?>();
var ids = new HashSet<string>();
foreach (var t in sm.Transitions)
{
if (!seen.Add(t.Trigger)) return false;
if (!string.IsNullOrWhiteSpace(t.Trigger.Id) && !ids.Add(t.Trigger.Id)) return false;
}
return true;
} Try / catch
try { await workflowRunner.RunAsync(stateMachineWorkflow); }
catch (InvalidOperationException ex) when (ex.Message.Contains("cannot share a Trigger activity"))
{
logger.LogError(ex, "State machine has duplicate trigger instances or Ids");
} Prevention
- Construct a fresh trigger instance per transition when building state machines in code.
- Check the designer for duplicated trigger Ids after copy/paste of transitions.
- Add a definition validation step that asserts trigger instance/Id uniqueness.
When it happens
Trigger: Defining a StateMachine where multiple transitions reuse one trigger activity instance, or where two trigger activities share the same Id string; validated when the state machine executes.
Common situations: Reusing a single trigger variable across several transitions for convenience; copying transitions in the designer which duplicates trigger Ids; programmatic construction loops that reuse a shared trigger object.
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
- The specified activity is not part of the workflow.
- A conversation ID is required. (Parameter 'conversation')
- A conversation user ID is required. (Parameter…
- A conversation ID is required. (Parameter 'conversation')
- A conversation user ID is required. (Parameter…
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/5099387f1bcb37ea.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Workflows.Core/Activities/StateMachine/Activities/StateMachine.cs:326
yield return transition.Action;
}
yield return _automaticTransitionContinuation;
}
private void EnsureSupportedTriggerIdentities()
{
var triggers = Transitions.Where(x => x.Trigger != null).Select(x => x.Trigger!).ToList();
var seenInstances = new HashSet<IActivity>(ReferenceEqualityComparer.Instance);
var seenIds = new HashSet<string>(StringComparer.Ordinal);
foreach (var trigger in triggers)
{
var sharesInstance = !seenInstances.Add(trigger);
var duplicatesId = !string.IsNullOrWhiteSpace(trigger.Id) && !seenIds.Add(trigger.Id);
if (sharesInstance || duplicatesId)
throw new InvalidOperationException("StateMachine transitions cannot share a Trigger activity in Elsa 3.8. Give each transition its own trigger activity with a unique ID.");
}
}
private void SetCurrentState(ActivityExecutionContext context, string? state)
{
CurrentState = state;
if (state == null)
context.RemoveProperty(CurrentStateProperty);
else
context.SetProperty(CurrentStateProperty, state);
}
private bool IsCurrentSource(ActivityExecutionContext context, Transition transition) => string.Equals(transition.From, GetCurrentState(context), StringComparison.Ordinal);
private static async Task<bool> EvaluateConditionAsync(ActivityExecutionContext context, Input<bool> condition)
{
var evaluator = context.GetRequiredService<IExpressionEvaluator>();View on GitHub (pinned to fe9217bdfa)