elsa-workflows/elsa-core · error · InvalidOperationException

Activity context is not a flowchart.

Error message

Activity context is not a flowchart.

What it means

Thrown by CancelInboundAncestorsAsync when the ActivityExecutionContext's current activity is not a Flowchart. This extension method only works on a flowchart context because it needs the flow graph to compute which ancestor activities to cancel. It is an internal invariant check guarding misuse of the extension.

Solutions

  1. Only call CancelInboundAncestorsAsync from code running on a Flowchart activity context.
  2. Guard with a type check first: if (context.Activity is Activities.Flowchart) await context.CancelInboundAncestorsAsync(activity);
  3. Use the Flowchart's own ActivityExecutionContext (the parent context), not a child activity's context.
  4. For ancestor cancellation in non-flowchart containers, implement your own traversal instead of this flowchart-specific extension.

Example fix

// before
await context.CancelInboundAncestorsAsync(activity); // throws when context.Activity is not a Flowchart
// after
if (context.Activity is Activities.Flowchart)
    await context.CancelInboundAncestorsAsync(activity);
Defensive patterns

Strategy: type-guard

Validate before calling

if (context.Activity is not Elsa.Workflows.Core.Activities.Flowchart)
    throw new InvalidOperationException("CancelInboundAncestorsAsync requires a Flowchart context.");
await context.CancelInboundAncestorsAsync(activity);

Type guard

static bool IsFlowchartContext(ActivityExecutionContext context) => context.Activity is Elsa.Workflows.Core.Activities.Flowchart;

Try / catch

try { await context.CancelInboundAncestorsAsync(activity); }
catch (InvalidOperationException ex) when (ex.Message == "Activity context is not a flowchart.")
{
    logger.LogWarning(ex, "Ancestor cancellation attempted on non-flowchart context {Type}", context.Activity?.Type);
}

Prevention

When it happens

Trigger: Calling context.CancelInboundAncestorsAsync(activity) from an ActivityExecutionContext whose Activity is not an Activities.Flowchart — e.g. calling from a child activity of the flowchart or from a non-flowchart workflow root.

Common situations: Custom activities reusing flowchart-internal cancellation logic; refactors that move code out of the Flowchart handler into a shared helper without checking the enclosing activity type.

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/1234094775473418. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.Workflows.Core/Activities/Flowchart/Extensions/ActivityExecutionContextExtensions.cs:73

        {
            return context.HasRunningActivityInstances()
                   || context.HasScheduledWork()
                   || context.HasUnconsumedTokens()
                   || context.HasFaultedChildren();
        }

        public FlowGraph GetFlowGraph()
        {
            // Store in TransientProperties so FlowChart is not persisted in WorkflowState
            var flowchart = (Activities.Flowchart)context.Activity;
            var startActivity = flowchart.GetStartActivity(context.WorkflowExecutionContext.TriggerActivityId);
            return context.TransientProperties.GetOrAdd(GraphTransientProperty, () => new FlowGraph(flowchart.Connections, startActivity));
        }

        public async Task CancelInboundAncestorsAsync(IActivity activity)
        {
            if(context.Activity is not Activities.Flowchart)
                throw new InvalidOperationException("Activity context is not a flowchart.");
        
            var flowGraph = context.GetFlowGraph();
            var ancestorActivities = flowGraph.GetAncestorActivities(activity);
            var inboundActivityExecutionContexts = context.WorkflowExecutionContext.ActivityExecutionContexts.Where(x => ancestorActivities.Contains(x.Activity) && x.ParentActivityExecutionContext == context).ToList();

            // Cancel each ancestor activity.
            foreach (var activityExecutionContext in inboundActivityExecutionContexts)
            {
                await activityExecutionContext.CancelActivityAsync();
            }
        }
        
        /// <summary>
        /// Checks if the flowchart has any unconsumed tokens.
        /// </summary>
        public bool HasUnconsumedTokens()
        {
            var flowchart = (Activities.Flowchart)context.Activity;

View on GitHub (pinned to fe9217bdfa)