elsa-workflows/elsa-core · critical · Exception

We lost a context. This could indicate a bug in a parent…

Error message

We lost a context. This could indicate a bug in a parent activity that completed before (some of) its child activities.

What it means

While extracting the state of active activity execution contexts, the extractor records each context's parent ID and verifies the parent still exists in the workflow execution context's collection. A missing parent means a parent activity's context was completed/removed while its child contexts remained active — an inconsistency that would produce unrestorable state — so the extractor throws with this diagnostic message.

Solutions

  1. Fix the offending parent activity to remain active until every child activityExecutionContext completes (defer completion until all children's callbacks/contexts are done).
  2. Update to the latest Elsa packages; several parent-completes-too-early bugs in built-in activities have been patched.
  3. Review custom activities for early context.Complete()/broken completion-callback wiring and use context.DeferCallbacks where children are scheduled.
  4. Identify the guilty activity from the exception context/logs and restructure the workflow (e.g. use a Parallel/Flowchart with correct completion semantics).
  5. If persisting mid-execution, ensure extraction happens only at safe suspension points where parent/child contexts are consistent.

Example fix

// before (parent activity ExecuteAsync)
foreach (var child in children) context.ScheduleActivity(child);
context.Complete(); // parent context dies before children -> lost parent
// after
foreach (var child in children)
    context.ScheduleActivity(child, completionCallback: _ =>
    {
        if (context.GetActivityExecutionContextCount() == 0) // or track pending children
            context.Complete();
    });
Defensive patterns

Strategy: try-catch

Validate before calling

var activeIds = context.GetActiveActivityExecutionContexts().Select(x => x.Id).ToHashSet();
bool parentsConsistent = context.GetActiveActivityExecutionContexts().All(x => x.ParentContext == null || activeIds.Contains(x.ParentContext.Id));

Try / catch

try { var state = extractor.Extract(context); } catch (Exception ex) { logger.LogCritical(ex, "State extraction failed: parent activity completed before children (workflow {InstanceId})", context.Id); throw; }

Prevention

When it happens

Trigger: Extract/persist/suspend a workflow instance when an active ActivityExecutionContext references a ParentContext ID that is no longer present in WorkflowExecutionContext.ActivityExecutionContexts — typically a parent activity completed before some of its children.

Common situations: Buggy custom or composite activities calling Complete() before all child contexts finished; race between parent completion and child scheduling; older Elsa versions with known parent-completion bugs in parallel/flow activities; resuming after partial state corruption.

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

Appendix: source

Thrown at src/modules/Elsa.Workflows.Core/Services/WorkflowStateExtractor.cs:301

        }

        var completionCallbacks = workflowExecutionContext.CompletionCallbacks.Select(x => new CompletionCallbackState(x.Owner.Id, x.Child.NodeId, x.CompletionCallback?.Method.Name, x.Tag));

        state.CompletionCallbacks = completionCallbacks.ToList();
    }

    private static void ExtractActiveActivityExecutionContexts(WorkflowState state, WorkflowExecutionContext workflowExecutionContext)
    {
        ActivityExecutionContextState CreateActivityExecutionContextState(ActivityExecutionContext activityExecutionContext)
        {
            var parentId = activityExecutionContext.ParentActivityExecutionContext?.Id;

            if (parentId != null)
            {
                var parentContext = activityExecutionContext.WorkflowExecutionContext.ActivityExecutionContexts.FirstOrDefault(x => x.Id == parentId);

                if (parentContext == null)
                    throw new("We lost a context. This could indicate a bug in a parent activity that completed before (some of) its child activities.");
            }

            var activityExecutionContextState = new ActivityExecutionContextState
            {
                Id = activityExecutionContext.Id,
                CallStackDepth = activityExecutionContext.CallStackDepth,
                ParentContextId = activityExecutionContext.ParentActivityExecutionContext?.Id,
                ScheduledActivityNodeId = activityExecutionContext.NodeId,
                OwnerActivityNodeId = activityExecutionContext.ParentActivityExecutionContext?.NodeId,
                Properties = activityExecutionContext.Properties,
                Metadata = activityExecutionContext.Metadata,
                ActivityState = activityExecutionContext.ActivityState,
                Status = activityExecutionContext.Status,
                IsExecuting = activityExecutionContext.IsExecuting,
                FaultCount = activityExecutionContext.AggregateFaultCount,
                StartedAt = activityExecutionContext.StartedAt,
                CompletedAt = activityExecutionContext.CompletedAt,
                Tag = activityExecutionContext.Tag,

View on GitHub (pinned to fe9217bdfa)