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
- Only call CancelInboundAncestorsAsync from code running on a Flowchart activity context.
- Guard with a type check first: if (context.Activity is Activities.Flowchart) await context.CancelInboundAncestorsAsync(activity);
- Use the Flowchart's own ActivityExecutionContext (the parent context), not a child activity's context.
- 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
- Only invoke flowchart-specific extensions from inside the Flowchart implementation.
- Check context.Activity type before using flow graph APIs.
- Keep flowchart-internal logic encapsulated instead of calling it from child activities.
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
- AlterationFaultCodes.PlanNotFound
- No alterations found in the transient properties.
- No alteration log found in the transient properties.
- BPMN element ' ' binds process activity ' ' as the work of…
- {fault.Code}
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)