{"record":{"id":"86a567e8577cb119","repo":"iOfficeAI/OfficeCLI","slug":"anchor-must-be-a-row-path-like-targetsheetname","errorCode":null,"errorMessage":"Anchor must be a row path like /{targetSheetName}/row[K], got: {anchorPath}","messagePattern":"Anchor must be a row path like /(.+?)/row\\[K\\], got: (.+?)","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Handlers/Excel/ExcelHandler.Add.cs","lineNumber":317,"sourceCode":"            var row = (rowIdx >= 1 && rowIdx <= allRows.Count ? allRows[PathIndex.ToArrayIndex(rowIdx)] : null)\n                ?? sheetData.Elements<Row>().FirstOrDefault(r => r.RowIndex?.Value == (uint)rowIdx)\n                ?? throw new ArgumentException($\"Row {rowIdx} not found\");\n\n            // Resolve --before / --after anchors to a 0-based document-order\n            // position in the target sheet. Anchor must be /<TargetSheet>/row[K].\n            // Resolved BEFORE removing the moved row so the anchor is found by\n            // its current position.\n            int? targetIndex = index;\n            string targetSheetName = string.IsNullOrEmpty(targetParentPath)\n                ? sheetName\n                : targetParentPath.TrimStart('/').Split('/', 2)[0];\n            if (targetIndex == null && position != null && (position.After != null || position.Before != null))\n            {\n                int FindAnchorRowPos(string anchorPath)\n                {\n                    var aSegs = anchorPath.TrimStart('/').Split('/', 2);\n                    if (aSegs.Length < 2)\n                        throw new ArgumentException(\n                            $\"Anchor must be a row path like /{targetSheetName}/row[K], got: {anchorPath}\");\n                    if (!aSegs[0].Equals(targetSheetName, StringComparison.OrdinalIgnoreCase))\n                        throw new ArgumentException(\n                            $\"Anchor sheet '{aSegs[0]}' must match target sheet '{targetSheetName}'\");\n                    var am = Regex.Match(aSegs[1], @\"^row\\[(\\d+)\\]$\");\n                    if (!am.Success)\n                        throw new ArgumentException(\n                            $\"Anchor must be a row path like /{targetSheetName}/row[K], got: {anchorPath}\");\n                    var anchorRowIdx = uint.Parse(am.Groups[1].Value);\n                    var pos = targetSheetData.Elements<Row>().ToList()\n                        .FindIndex(r => r.RowIndex?.Value == anchorRowIdx);\n                    if (pos < 0)\n                        throw new ArgumentException($\"Anchor row {anchorRowIdx} not found in {targetSheetName}\");\n                    return pos;\n                }\n                if (position.Before != null) targetIndex = FindAnchorRowPos(position.Before);\n                else targetIndex = FindAnchorRowPos(position.After!) + 1;\n            }","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Handlers/Excel/ExcelHandler.Add.cs#L299-L335","documentation":"Thrown by the row-anchor resolver when a --before/--after anchor path has no second segment after trimming (aSegs.Length < 2), i.e. the anchor is just a sheet like \"/Sheet1\" with no '/row[K]'. Row anchors must be full element paths so the handler can locate the sibling row.","triggerScenarios":"InsertPosition.BeforeElement(\"/Sheet1\") or AfterElement(\"/Sheet1\") for a row move; anchor passed as a bare sheet name; missing '/row[K]' suffix.","commonSituations":"User reused a sheet-level anchor (valid for whole-sheet reorder) for a row move; truncation when building the anchor path.","solutions":["Provide the full anchor: \"/<TargetSheet>/row[K]\".","Build anchors from a verified row path, not a sheet path.","For a same-sheet move, ensure targetParentPath is null so the anchor sheet defaults correctly."],"exampleFix":"// before\nh.Move(\"/Sheet1/row[3]\", null, InsertPosition.AfterElement(\"/Sheet1\")); // no row segment\n// after\nh.Move(\"/Sheet1/row[3]\", null, InsertPosition.AfterElement(\"/Sheet1/row[5]\"));","handlingStrategy":"validation","validationCode":"string AssertRowAnchor(string anchor)\n{\n    var segs = anchor.TrimStart('/').Split('/', 2);\n    if (segs.Length < 2)\n        throw new ArgumentException($\"Row anchor needs a /sheet/row[K] form: '{anchor}'\");\n    return anchor;\n}\nvar after = pos?.After is null ? null : AssertRowAnchor(pos.After);\nvar before = pos?.Before is null ? null : AssertRowAnchor(pos.Before);","typeGuard":"static bool IsFullElementPath(string p) =>\n    p.TrimStart('/').Split('/', 2).Length >= 2;","tryCatchPattern":null,"preventionTips":["Always include the /row[K] suffix on row anchors.","Build anchors from a verified row path, not a sheet path.","Reuse a shared validator for both before/after anchors."],"tags":["excel","openxml","move","row-move","anchor","malformed-path"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}