iOfficeAI/OfficeCLI · error · ArgumentException

Anchor must be a row path like /{targetSheetName}/row[K], go

Error message

Anchor must be a row path like /{targetSheetName}/row[K], got: {anchorPath}

What it means

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.

Source

Thrown at src/officecli/Handlers/Excel/ExcelHandler.Add.cs:317

            var row = (rowIdx >= 1 && rowIdx <= allRows.Count ? allRows[PathIndex.ToArrayIndex(rowIdx)] : null)
                ?? sheetData.Elements<Row>().FirstOrDefault(r => r.RowIndex?.Value == (uint)rowIdx)
                ?? throw new ArgumentException($"Row {rowIdx} not found");

            // Resolve --before / --after anchors to a 0-based document-order
            // position in the target sheet. Anchor must be /<TargetSheet>/row[K].
            // Resolved BEFORE removing the moved row so the anchor is found by
            // its current position.
            int? targetIndex = index;
            string targetSheetName = string.IsNullOrEmpty(targetParentPath)
                ? sheetName
                : targetParentPath.TrimStart('/').Split('/', 2)[0];
            if (targetIndex == null && position != null && (position.After != null || position.Before != null))
            {
                int FindAnchorRowPos(string anchorPath)
                {
                    var aSegs = anchorPath.TrimStart('/').Split('/', 2);
                    if (aSegs.Length < 2)
                        throw new ArgumentException(
                            $"Anchor must be a row path like /{targetSheetName}/row[K], got: {anchorPath}");
                    if (!aSegs[0].Equals(targetSheetName, StringComparison.OrdinalIgnoreCase))
                        throw new ArgumentException(
                            $"Anchor sheet '{aSegs[0]}' must match target sheet '{targetSheetName}'");
                    var am = Regex.Match(aSegs[1], @"^row\[(\d+)\]$");
                    if (!am.Success)
                        throw new ArgumentException(
                            $"Anchor must be a row path like /{targetSheetName}/row[K], got: {anchorPath}");
                    var anchorRowIdx = uint.Parse(am.Groups[1].Value);
                    var pos = targetSheetData.Elements<Row>().ToList()
                        .FindIndex(r => r.RowIndex?.Value == anchorRowIdx);
                    if (pos < 0)
                        throw new ArgumentException($"Anchor row {anchorRowIdx} not found in {targetSheetName}");
                    return pos;
                }
                if (position.Before != null) targetIndex = FindAnchorRowPos(position.Before);
                else targetIndex = FindAnchorRowPos(position.After!) + 1;
            }

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Provide the full anchor: "/<TargetSheet>/row[K]".
  2. Build anchors from a verified row path, not a sheet path.
  3. For a same-sheet move, ensure targetParentPath is null so the anchor sheet defaults correctly.

Example fix

// before
h.Move("/Sheet1/row[3]", null, InsertPosition.AfterElement("/Sheet1")); // no row segment
// after
h.Move("/Sheet1/row[3]", null, InsertPosition.AfterElement("/Sheet1/row[5]"));
Defensive patterns

Strategy: validation

Validate before calling

string AssertRowAnchor(string anchor)
{
    var segs = anchor.TrimStart('/').Split('/', 2);
    if (segs.Length < 2)
        throw new ArgumentException($"Row anchor needs a /sheet/row[K] form: '{anchor}'");
    return anchor;
}
var after = pos?.After is null ? null : AssertRowAnchor(pos.After);
var before = pos?.Before is null ? null : AssertRowAnchor(pos.Before);

Type guard

static bool IsFullElementPath(string p) =>
    p.TrimStart('/').Split('/', 2).Length >= 2;

Prevention

When it happens

Trigger: InsertPosition.BeforeElement("/Sheet1") or AfterElement("/Sheet1") for a row move; anchor passed as a bare sheet name; missing '/row[K]' suffix.

Common situations: User reused a sheet-level anchor (valid for whole-sheet reorder) for a row move; truncation when building the anchor path.

Related errors


AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13). Data as JSON: /api/errors/86a567e8577cb119. Report an issue: GitHub.