iOfficeAI/OfficeCLI · error · ArgumentException

--prop shift={shiftVal} not valid for add cell. Use 'right'

Error message

--prop shift={shiftVal} not valid for add cell. Use 'right' or 'down'.

What it means

The `shift` property on Add cell mimics Excel UI's 'Insert Cells > Shift cells right / down', pushing existing cells in the same row (right) or column (down) by 1 before materializing the new cell. Only the literal tokens 'right' and 'down' (case-insensitive) are accepted. Any other value is rejected before the shift so no cells are moved on a failed call.

Source

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

        if (rowIndexFromPath.HasValue)
        {
            var refRowMatch = Regex.Match(cellRef, @"^([A-Z]+)(\d+)$", RegexOptions.IgnoreCase);
            if (refRowMatch.Success && uint.Parse(refRowMatch.Groups[2].Value) != rowIndexFromPath.Value)
                Console.Error.WriteLine(
                    $"warning: path row[{rowIndexFromPath.Value}] does not match cell ref '{cellRef}' row; using ref's row.");
        }
        // --prop shift=right|down: before materializing the new cell, push
        // existing cells in the same row (right) or column (down) by 1.
        // Mirrors Excel UI's "Insert Cells > Shift cells right / down".
        // Same scope cap as RemoveCellWithShift: only intra-row/col cellRefs
        // are rewritten — formulas, mergeCells, CF/DV/hyperlinks/tables that
        // span the affected row/col are NOT adjusted. For full row/col insert
        // with all relations, use add --type row / --type col.
        if (properties.TryGetValue("shift", out var shiftVal) && !string.IsNullOrEmpty(shiftVal))
        {
            var shiftDir = shiftVal.ToLowerInvariant();
            if (shiftDir is not ("right" or "down"))
                throw new ArgumentException(
                    $"--prop shift={shiftVal} not valid for add cell. Use 'right' or 'down'.");
            var (shiftCol, shiftRow) = ParseCellReference(cellRef);
            var shiftColIdx = ColumnNameToIndex(shiftCol);
            if (shiftDir == "right")
                ShiftCellsRightInRow(cellSheetData, (uint)shiftRow, shiftColIdx);
            else
                ShiftCellsDownInColumn(cellSheetData, shiftCol, shiftRow);
        }

        // Atomicity: validate a type=boolean value BEFORE FindOrCreateCell
        // appends the cell to the sheet. A throw AFTER the cell is created
        // used to leave a corrupt <c t="b"><v>garbage</v></c> persisted on
        // disk (real Excel then refuses the file, 0x800A03EC) even though the
        // Add reported an error. The later in-switch check stays as a
        // defense-in-depth guard.
        {
            var upfrontType = properties.GetValueOrDefault("type")?.ToLowerInvariant();
            var upfrontValue = (properties.GetValueOrDefault("value")

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Use exactly 'right' (shift cells in the same row to the right) or 'down' (shift cells in the same column down).
  2. Trim and normalize the value to lowercase before passing it as the shift prop.
  3. For full row/column insertion with all relations, use add --type row / add --type col instead of cell shift.

Example fix

// before
handler.Add("/Sheet1/B2", "cell", null, new() { ["value"] = "x", ["shift"] = "left" });
// after
handler.Add("/Sheet1/B2", "cell", null, new() { ["value"] = "x", ["shift"] = "right" });
Defensive patterns

Strategy: validation

Validate before calling

if (props.TryGetValue("shift", out var s))
{
    var d = s.Trim().ToLowerInvariant();
    if (d != "right" && d != "down")
        throw new ArgumentException("shift must be 'right' or 'down'");
    props["shift"] = d;
}
h.Add(parentPath, "cell", pos, props);

Type guard

static bool IsValidShift(string s) => s.Trim().ToLowerInvariant() is "right" or "down";

Try / catch

try { h.Add(parentPath, "cell", pos, props); }
catch (ArgumentException ex) when (ex.Message.Contains("not valid for add cell"))
{ /* correct to right/down */ }

Prevention

When it happens

Trigger: Add("/Sheet1/A1","cell",pos,{["shift"]="left"}); shift="up"; shift="horizontal"; shift="RIGHT " with trailing space after ToLowerInvariant is applied but the match is exact so 'right ' (with space) fails.

Common situations: Expecting four-directional shift (Excel insert only offers right/down for a single-cell insert; left/up are Remove operations); passing a localized word instead of the English token; trailing whitespace from a config file.

Related errors


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