iOfficeAI/OfficeCLI · error · ArgumentException
One of --index, --after, or --before is required when moving
Error message
One of --index, --after, or --before is required when moving a sheet
What it means
Thrown when moving a whole sheet (sourcePath with no second segment, e.g. "/Sheet1") but position is null or carries none of Index/After/Before. Unlike element moves where a null position means 'append', a sheet reorder requires an explicit target because the handler refuses to guess a sheet's new location.
Source
Thrown at src/officecli/Handlers/Excel/ExcelHandler.Add.cs:210
Sheet? afterAnchor = null, beforeAnchor = null;
if (position?.After != null)
{
var anchorName = ExtractAnchorSheetName(position.After);
afterAnchor = sheets.Elements<Sheet>().FirstOrDefault(s =>
string.Equals(s.Name?.Value, anchorName, StringComparison.OrdinalIgnoreCase))
?? throw new ArgumentException($"After anchor not found: {position.After}");
}
else if (position?.Before != null)
{
var anchorName = ExtractAnchorSheetName(position.Before);
beforeAnchor = sheets.Elements<Sheet>().FirstOrDefault(s =>
string.Equals(s.Name?.Value, anchorName, StringComparison.OrdinalIgnoreCase))
?? throw new ArgumentException($"Before anchor not found: {position.Before}");
}
else if (index == null)
{
throw new ArgumentException("One of --index, --after, or --before is required when moving a sheet");
}
// Self-move guard: moving a sheet after/before itself is a no-op.
// Removing first detaches sheetEl, then InsertAfterSelf/InsertBeforeSelf
// throws "parent is null" and the sheet is lost (data loss).
if (ReferenceEquals(afterAnchor, sheetEl) || ReferenceEquals(beforeAnchor, sheetEl))
return $"/{sheetName}";
// localSheetId on <definedName> is a 0-based position into
// <sheets>; capture the pre-move order so scoped names can be
// remapped to the sheets' new positions after the reorder.
var preMoveOrder = sheets.Elements<Sheet>().ToList();
sheetEl.Remove();
if (afterAnchor != null)
{
afterAnchor.InsertAfterSelf(sheetEl);View on GitHub (pinned to 1ced45e900)
Solutions
- Pass InsertPosition.AtIndex(n) for an absolute 1-based slot.
- Pass InsertPosition.AfterElement("/<sheet>") or BeforeElement("/<sheet>") for a relative reorder.
- If you meant 'move to end', compute the last index from the sheet count and pass AtIndex(count).
Example fix
// before
h.Move("/Sheet1", null, null); // no target -> throws
// after
h.Move("/Sheet1", null, InsertPosition.AtIndex(0)); // move to first position Defensive patterns
Strategy: validation
Validate before calling
// Whole-sheet move (no element segment) requires an explicit target.
bool isSheetMove = sourcePath.TrimStart('/').Split('/', 2).Length < 2;
bool hasTarget = pos is { Index: not null } || pos?.After != null || pos?.Before != null;
if (isSheetMove && !hasTarget)
throw new InvalidOperationException("Sheet reorder requires --index, --after, or --before"); Type guard
static bool HasMoveTarget(InsertPosition? p) =>
p is { Index: not null } || (p?.After != null) || (p?.Before != null); Prevention
- Never pass null position for a whole-sheet Move.
- Use InsertPosition.AtIndex/AfterElement/BeforeElement factories so a target is always set.
- Compute 'move to end' explicitly as AtIndex(sheetCount).
When it happens
Trigger: h.Move("/Sheet1", null, null); or Move("/Sheet1", null, new InsertPosition()) with all fields null; passing a position object whose Index is null and After/Before are null.
Common situations: Caller assumes whole-sheet Move defaults to append like Add does; refactored code dropped the position argument; CLI invocation omitted --index/--after/--before.
Related errors
- After anchor not found: {position.After}
- Before anchor not found: {position.Before}
- Sheet not found: {sheetName}
- Workbook has no sheets element
- Sheet has no data
AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13).
Data as JSON: /api/errors/3090706749795502.
Report an issue: GitHub.