{"record":{"id":"6e0256a6f61f8710","repo":"iOfficeAI/OfficeCLI","slug":"xml-is-required-for-insertafter","errorCode":null,"errorMessage":"--xml is required for insertafter","messagePattern":"--xml is required for insertafter","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/RawXmlHelper.cs","lineNumber":198,"sourceCode":"                    break;\n\n                case \"insertbefore\" or \"before\":\n                    if (xml == null) throw new ArgumentException(\"--xml is required for insertbefore\");\n                    RequireParent(node, \"insertbefore\");\n                    var beforeFragment = ParseFragment(xml, xDoc);\n                    // AddBeforeSelf lands each element immediately before the\n                    // anchor, i.e. AFTER everything inserted so far — forward\n                    // iteration preserves source order. (The reverse idiom is\n                    // insertafter-only; reversing here flipped a multi-element\n                    // fragment, splitting bookmarkStart/End pairs so the id\n                    // balancer synthesized a duplicate w:id end marker.)\n                    foreach (var el in beforeFragment)\n                        node.AddBeforeSelf(el);\n                    affected++;\n                    break;\n\n                case \"insertafter\" or \"after\":\n                    if (xml == null) throw new ArgumentException(\"--xml is required for insertafter\");\n                    RequireParent(node, \"insertafter\");\n                    var afterFragment = ParseFragment(xml, xDoc);\n                    // AddAfterSelf inserts immediately after `node`, so calling it\n                    // repeatedly against the SAME anchor REVERSES a multi-element\n                    // fragment (start,end → node,end,start). Iterate in REVERSE and\n                    // keep anchoring to `node`, mirroring insertbefore — each element\n                    // lands right after `node`, yielding source order. (Chaining the\n                    // anchor off the just-added node does NOT work: AddAfterSelf clones\n                    // a parented element, so the loop variable still points at the\n                    // detached fragment node, the chain breaks, and only the first\n                    // element reaches the document — silently dropping the rest of a\n                    // multi-marker fragment, e.g. a second tr-level bookmark.) A\n                    // reversed start/end pair also desynced the id-balancer into\n                    // duplicate bookmark ids.\n                    foreach (var el in afterFragment.AsEnumerable().Reverse())\n                        node.AddAfterSelf(el);\n                    affected++;\n                    break;","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/RawXmlHelper.cs#L180-L216","documentation":"Thrown when the action is 'insertafter' (or alias 'after') but the xml argument is null. Insertafter places the fragment as a following sibling of each matched element; it additionally calls RequireParent. The implementation iterates the fragment in reverse to preserve source order when anchoring multiple elements after the same node. Without a fragment there is nothing to insert.","triggerScenarios":"RawXmlHelper.Execute(rootElement, xpath, \"insertafter\", null) or RawXmlHelper.Execute(rootElement, xpath, \"after\", null). XPath matched, code entered the insertafter/after case, xml == null.","commonSituations":"Caller invokes raw-set with action=insertafter but omits --xml. Caller confused 'append' (last child) with 'insertafter' (following sibling). Conditionally-built xml argument evaluated to null.","solutions":["Provide a valid XML fragment to insert as a following sibling of each matched element.","Use 'insertafter' for sibling-level insertion; use 'append' for child-level insertion (last child).","If the matched element is the document root, insertafter cannot work (no parent) — use a different action."],"exampleFix":"// before\nRawXmlHelper.Execute(root, xpath, \"insertafter\", null);\n\n// after\nRawXmlHelper.Execute(root, xpath, \"insertafter\", \"<w:p><w:r><w:t>Inserted after</w:t></w:r></w:p>\");","handlingStrategy":"validation","validationCode":"var isInsertAfter = action.Equals(\"insertafter\", StringComparison.OrdinalIgnoreCase)\n                    || action.Equals(\"after\", StringComparison.OrdinalIgnoreCase);\nif (isInsertAfter && string.IsNullOrEmpty(xml))\n    throw new ArgumentException(\"--xml is required for insertafter\");\n\nRawXmlHelper.Execute(rootElement, xpath, action, xml);","typeGuard":null,"tryCatchPattern":"try\n{\n    RawXmlHelper.Execute(rootElement, xpath, \"insertafter\", xml);\n}\ncatch (ArgumentException ex) when (ex.Message.Contains(\"--xml is required for insertafter\"))\n{\n    Console.Error.WriteLine(\"Provide an XML fragment to insert as a following sibling.\");\n}","preventionTips":["Provide the xml fragment for insertafter (following sibling).","Remember: insertafter = sibling, append = last child.","Ensure the matched node has a parent — insertafter can't work on root."],"tags":["xml","xpath","openxml","argument","raw-set"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}