{"record":{"id":"dbfb5c76cdb91580","repo":"iOfficeAI/OfficeCLI","slug":"cannot-remove-container-element-path-it-is-a","errorCode":null,"errorMessage":"Cannot remove container element '{path}': it is a required structural element of the document.","messagePattern":"Cannot remove container element '(.+?)': it is a required structural element of the document\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Handlers/Excel/ExcelHandler.Remove.cs","lineNumber":27,"sourceCode":"using XDR = DocumentFormat.OpenXml.Drawing.Spreadsheet;\nusing X14 = DocumentFormat.OpenXml.Office2010.Excel;\nusing OfficeCli.Core;\n\nnamespace OfficeCli.Handlers;\n\npublic partial class ExcelHandler\n{\n    public string? Remove(string path, Dictionary<string, string>? properties = null)\n    {\n        // Phase 4: trackChange.* is Word-only. Silently ignored here.\n        Modified = true;\n        // CONSISTENCY(container-remove-guard): reject removal of the\n        // workbook root up front. Sheet-level removal has its own guard\n        // (can't remove last sheet) further down and is a legitimate op;\n        // /workbook is not.\n        if (!string.IsNullOrEmpty(path)\n            && path.TrimEnd('/').Equals(\"/workbook\", StringComparison.OrdinalIgnoreCase))\n            throw new ArgumentException(\n                $\"Cannot remove container element '{path}': it is a required structural element of the document.\");\n\n        // Batch Remove: a selector path (not starting with '/') → Query → Remove\n        // each match, mirroring ExcelHandler.Set's selector branch. Row removals\n        // are TRUE shift-deletes (rows below shift up — see \"row[N] — true shift\n        // delete\"), so multiple matched rows MUST be removed in DESCENDING row\n        // order: deleting /Sheet/row[2] first renumbers the old row[4] to row[3]\n        // and the next delete would hit the wrong row. Non-row targets carry\n        // index 0 and keep a stable relative order.\n        if (!string.IsNullOrEmpty(path)\n            && (!path.StartsWith(\"/\") || Core.AttributeFilter.IsContentFilterPath(path)))\n        {\n            // Narrow via the shared engine (same as Set / query): pure-AND on the\n            // legacy path, `or` selectors queried bracket-stripped then narrowed by\n            // the boolean expression tree. The IsContentFilterPath arm routes a\n            // `/`-scoped content filter (`/Sheet1/cell[value>5 or value<1]`) here\n            // too, matching the Set dispatch — query, set and remove now agree on\n            // every selector shape.","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Handlers/Excel/ExcelHandler.Remove.cs#L9-L45","documentation":"Thrown by ExcelHandler.Remove when the path equals '/workbook' (case-insensitive, trailing slash trimmed). The workbook root is a required structural container and cannot be deleted; removing it would destroy the document. Sheet-level removal is a legitimate op with its own guard (cannot remove the last sheet) elsewhere, but /workbook is rejected up front.","triggerScenarios":"remove /workbook, or remove /workbook/. Any removal request whose normalized path is exactly the workbook root.","commonSituations":"Programmatic removal loops that walk up to the root; misunderstanding the path hierarchy and trying to clear the whole document in one call.","solutions":["Target a real removable element: a sheet (remove /Sheet1), a cell, a row, or a shape.","If you want an empty workbook, create a new one rather than removing the root.","In removal loops, stop at the sheet level and never synthesize a '/workbook' path."],"exampleFix":"// before\nremove /workbook\n// after\nremove /Sheet1   // remove a specific sheet (last-sheet guard applies)","handlingStrategy":"validation","validationCode":"def assert_not_workbook_root(path):\n    norm = path.strip().rstrip('/').lower()\n    assert norm != '/workbook', 'cannot remove the workbook root — target a sheet or element instead'\n\nassert_not_workbook_root('/workbook')","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Never synthesize a '/workbook' removal path in loops.","Stop removal walks at the sheet level.","To start fresh, create a new workbook instead of removing the root."],"tags":["excel","remove","structural-guard","container","workbook"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}