{"record":{"id":"fd766316a02966b2","repo":"elsa-workflows/elsa-core","slug":"subprocess-element-subprocess-elementid-of-process-process","errorCode":null,"errorMessage":"Subprocess element '{subprocess.ElementId}' of process '{process.ProcessId}' carries no bindingRef, so the body stored for it cannot be written back with it. The document does not carry a subprocess's body, so writing it without one would silently empty the subprocess. Send the element with the bindingRef the document GET returned.","messagePattern":"Subprocess element '(.+?)' of process '(.+?)' carries no bindingRef, so the body stored for it cannot be written back with it\\. The document does not carry a subprocess's body, so writing it without one would silently empty the subprocess\\. Send the element with the bindingRef the document GET returned\\.","errorType":"exception","errorClass":"BpmnInterchangeException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.Bpmn.Interchange/Services/BpmnInterchangeDocumentService.cs","lineNumber":670,"sourceCode":"            .Where(element => element.LoopCharacteristics is not null)\n            .Select(element => element.ElementId)\n            .ToHashSet(StringComparer.Ordinal);\n\n        var kept = new List<BpmnWorkBinding>();\n\n        foreach (var process in document.Processes)\n        {\n            foreach (var subprocess in process.Elements.Where(element => element.ElementType == BpmnElementTypes.SubProcess))\n            {\n                // Last match wins, as it does inside the writer itself, should a malformed document repeat an id.\n                var body = storedBindings.OfType<BpmnWorkBinding.NestedProcess>().LastOrDefault(nested => nested.ElementId == subprocess.ElementId);\n\n                if (body is null)\n                    continue;\n\n                if (subprocess.BindingRef is null)\n                {\n                    throw new BpmnInterchangeException(\n                        $\"Subprocess element '{subprocess.ElementId}' of process '{process.ProcessId}' carries no bindingRef, so the body stored for it cannot be written back with it. \"\n                        + \"The document does not carry a subprocess's body, so writing it without one would silently empty the subprocess. Send the element with the bindingRef the document GET returned.\");\n                }\n\n                kept.Add(HandOver(body) with { BindingRef = subprocess.BindingRef });\n                KeepEverythingBoundInside(body.ElementId);\n            }\n        }\n\n        return kept;\n\n        // A nested process's bindings name the subprocess element's id as their process (see BpmnWorkBinding.ProcessId).\n        void KeepEverythingBoundInside(string scopeId)\n        {\n            foreach (var binding in storedBindings.Where(binding => binding.ProcessId == scopeId))\n            {\n                if (binding is not BpmnWorkBinding.NestedProcess nested)\n                {","sourceCodeStart":652,"sourceCodeEnd":688,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Bpmn.Interchange/Services/BpmnInterchangeDocumentService.cs#L652-L688","documentation":"When re-importing a document, StoredNestedScopesStillDeclaredBy carries across stored subprocess bodies to the new document. If an incoming subprocess element carries no bindingRef, the stored body cannot be reattached — writing it without one would silently empty the subprocess — so BpmnInterchangeException is thrown. The document format deliberately excludes subprocess bodies, so bindingRef is the only way to link the element back to its stored body.","triggerScenarios":"PUTting/importing a BPMN document in which a subprocess element (subprocess.ElementId within process.ProcessId) that has a stored body omits bindingRef — e.g. the client hand-built the XML instead of echoing the GET response.","commonSituations":"Manually authoring or transforming the BPMN XML and dropping the bindingRef attribute, running an XML transformation/normalizer that strips unknown attributes, or copying subprocess elements between documents without their bindingRefs.","solutions":["Send the subprocess element with the bindingRef exactly as the document GET returned it","Start each edit from the document returned by GET rather than hand-crafted XML","Extend your XML transformations to preserve the bindingRef attribute on subprocess elements"],"exampleFix":"// before\n<subProcess id=\"Sub1\"/>\n// after\n<subProcess id=\"Sub1\" bindingRef=\"stored-body-ref-from-GET\"/>","handlingStrategy":"validation","validationCode":"foreach (var sp in document.Processes.SelectMany(p => p.Subprocesses))\n    if (sp.BindingRef is null) throw new InvalidOperationException($\"Subprocess {sp.ElementId} is missing bindingRef\");","typeGuard":null,"tryCatchPattern":"try { await service.ImportAsync(id, doc); }\ncatch (BpmnInterchangeException ex) when (ex.Message.Contains(\"bindingRef\")) { restoreBindingRefsFromGetResponse(); }","preventionTips":["Always edit from the GET response document, never hand-built XML","Preserve all attributes (especially bindingRef) in XML transformations","Add a schema check that subprocesses with stored bodies carry bindingRef before import"],"tags":["bpmn","subprocess","binding-ref","validation"],"backgroundTag":"schema-validation-failed","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"}