elsa-workflows/elsa-core · error · Exception

Target context activity must be this flowchart

Error message

Target context activity must be this flowchart

What it means

ProcessChildCompletedAsync is a completion callback that must be invoked with an ActivityExecutionContext whose Activity is the Flowchart instance itself. If the context belongs to a different activity, the internal counter/bookkeeping state would be corrupted, so the code throws an InvalidOperationException immediately. This is an internal invariant check, not a user-facing validation.

Solutions

  1. Ensure the completion callback is registered on the flowchart's own ActivityExecutionContext, not a child's.
  2. If subclassing Flowchart, call the public/protected scheduling APIs instead of invoking the private handler with a foreign context.
  3. Re-check that activities are scheduled with scheduleWorkOptions tied to the flowchart context.
  4. Update Elsa packages together (core and flows) if a mixed-version runtime mismatches callback contexts.

Example fix

// before
scheduleWorkOptions = new() { CompletionCallback = OnChildCompletedAsync }; // callback captures child context
// after
flowchartContext.ScheduleActivity(activity, OnChildCompletedAsync); // Elsa binds the flowchart context as the first parameter
Defensive patterns

Strategy: type-guard

Validate before calling

if (context.Activity is not Flowchart) throw new InvalidOperationException("Completion callback must be bound to the Flowchart context.");

Type guard

static bool IsFlowchartContext(ActivityExecutionContext ctx) => ctx.Activity is Flowchart;

Try / catch

try { await ProcessChildCompletedAsync(flowchartContext, completedActivity, completedActivityContext, outcomes); }
catch (InvalidOperationException ex) when (ex.Message == "Target context activity must be this flowchart")
{
    logger.LogError(ex, "Flowchart completion callback invoked with mismatched activity context.");
}

Prevention

When it happens

Trigger: A child activity's Completed/CompletedCallback resolves to the ProcessChildCompletedAsync handler while the callback context was captured for a different activity; typically caused by custom Flowchart subclasses or reused delegates wiring OnChildCompletedCounterBasedLogicAsync to the wrong ActivityExecutionContext.

Common situations: Custom flowchart-like composite activities copying Elsa's Flowchart sample code but passing the wrong execution context to the completion callback; version drift where Flowchart internals changed but derived classes call the private handler directly via reflection.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13). Data as JSON: /api/errors/8a2de56b4f56415d. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/Flowchart.Counters.cs:125

        return rootActivity;
    }

    private FlowGraph GetFlowGraph(ActivityExecutionContext context)
    {
        // Store in TransientProperties so FlowChart is not persisted in WorkflowState 
        return context.TransientProperties.GetOrAdd(GraphTransientProperty, () => new FlowGraph(Connections, GetStartActivity(context)));
    }

    private FlowScope GetFlowScope(ActivityExecutionContext context)
    {
        return context.GetProperty(ScopeProperty, () => new FlowScope());
    }

    private async ValueTask ProcessChildCompletedAsync(ActivityExecutionContext flowchartContext, IActivity completedActivity, ActivityExecutionContext completedActivityContext, Outcomes outcomes)
    {
        if (flowchartContext.Activity != this)
        {
            throw new("Target context activity must be this flowchart");
        }

        // If the completed activity's status is anything but "Completed", do not schedule its outbound activities.
        if (completedActivityContext.Status != ActivityStatus.Completed)
        {
            return;
        }

        // If the complete activity is a terminal node, complete the flowchart immediately.
        if (completedActivity is ITerminalNode)
        {
            await flowchartContext.CompleteActivityAsync();
            return;
        }

        // Schedule the outbound activities
        var flowGraph = GetFlowGraph(flowchartContext);
        var flowScope = GetFlowScope(flowchartContext);

View on GitHub (pinned to fe9217bdfa)