{"record":{"id":"ad4f7b51e2207f89","repo":"elsa-workflows/elsa-core","slug":"bpmn-element-start-elementid-binds-process-activity-nested","errorCode":null,"errorMessage":"BPMN element '{start.ElementId}' binds process activity '{nested.Id}' as the work of scope '{process.Id}', but that activity declares itself the workflow's root scope. A nested scope's start events are internal to the process around it, not workflow entry points, so it must not be marked as able to start the workflow.","messagePattern":"BPMN element '(.+?)' binds process activity '(.+?)' as the work of scope '(.+?)', but that activity declares itself the workflow's root scope\\. A nested scope's start events are internal to the process around it, not workflow entry points, so it must not be marked as able to start the workflow\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.Bpmn/Hosting/BpmnCommandApplier.cs","lineNumber":44,"sourceCode":"    /// Applies a command list <b>in the order returned</b>.\n    /// </summary>\n    /// <remarks>\n    /// The ordering carries meaning and is not an implementation detail. An interrupting boundary event emits the\n    /// boundary path's <c>StartWork</c> <i>before</i> the teardown that retires the host it interrupted, and a host\n    /// that tidied up first would be applying a different process.\n    /// </remarks>\n    public async ValueTask ApplyAsync(IReadOnlyList<BpmnHostCommand> commands)\n    {\n        // Refused before anything in the batch is applied. Applying commands one at a time and refusing only once\n        // the offending StartWork is reached would leave earlier commands in the same batch already applied and\n        // saved via memory.SaveWork() below — and under ContinueWithIncidentsStrategy that throw is absorbed into\n        // an incident rather than surfaced, so the workflow would carry on with a half-applied batch and half-saved\n        // memory instead of the refusal stopping it clean.\n        foreach (var start in commands.OfType<BpmnHostCommand.StartWork>())\n        {\n            if (process.FindWorkActivity(start.BindingRef) is BpmnProcess { IsRootScope: true } nested)\n            {\n                throw new InvalidOperationException(\n                    $\"BPMN element '{start.ElementId}' binds process activity '{nested.Id}' as the work of scope '{process.Id}', but that activity declares itself the workflow's root scope. \"\n                    + \"A nested scope's start events are internal to the process around it, not workflow entry points, so it must not be marked as able to start the workflow.\");\n            }\n        }\n\n        foreach (var command in commands)\n        {\n            switch (command)\n            {\n                case BpmnHostCommand.StartWork start:\n                    await StartWorkAsync(start);\n                    break;\n                case BpmnHostCommand.CancelWorkSubtree cancel:\n                    await CancelWorkSubtreeAsync(cancel);\n                    break;\n                case BpmnHostCommand.SignalEnclosingScope signal:\n                    await SignalEnclosingScopeAsync(signal);\n                    break;","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Bpmn/Hosting/BpmnCommandApplier.cs#L26-L62","documentation":"BpmnCommandApplier.ApplyAsync pre-scans StartWork commands and refuses a command that binds a process activity marked as the workflow's root scope (IsRootScope) as nested work of another scope. Nested scopes' start events are internal and must never act as workflow entry points. It throws InvalidOperationException naming the BPMN element, the bound activity, and the owning scope.","triggerScenarios":"Applying a host command batch (via ApplyAsync) containing BpmnHostCommand.StartWork whose BindingRef resolves to a BpmnProcess with IsRootScope == true while nested under another process.","commonSituations":"Corrupted or hand-built command batches; a bug in the interpreter's binding derivation; model where a root process is incorrectly referenced as a subprocess of another process.","solutions":["Fix the model so the referenced process is not marked as root scope but is genuinely nested.","Regenerate the command batch so StartWork only binds non-root child activities.","Check the version alignment between the interpreter producing commands and the host applying them."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"foreach (var c in commands.OfType<BpmnHostCommand.StartWork>())\n    if (process.FindWorkActivity(c.BindingRef) is BpmnProcess { IsRootScope: true }) throw new InvalidOperationException(\"Batch binds a root scope as nested work.\");","typeGuard":null,"tryCatchPattern":"try { await applier.ApplyAsync(context, commands); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"root scope\")) { log.LogError(ex, \"Invalid StartWork binding\"); throw; }","preventionTips":["Keep model nesting consistent: root processes are never bound as children.","Regenerate command batches after model edits.","Pin interpreter and host to the same version."],"tags":["bpmn","host","invalid-state"],"backgroundTag":"invalid-state-transition","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}