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

  1. Give each transition its own trigger activity instance with a unique Id.
  2. If multiple transitions should react to the same event, create separate trigger instances (one per transition).
  3. De-duplicate trigger Ids across transitions in the state machine definition.
  4. 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

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


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)