PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Exception

Rows to be deleted should at least start from row 1.

Error message

Rows to be deleted should at least start from row 1.

What it means

removeRow(int $row, int $numberOfRows = 1) deletes rows starting at $row. The start row must be >= 1; negative $numberOfRows values are normalized internally (removeRow(5, -2) removes rows 3-4), but a start row below 1 always throws before any shifting happens.

Source

Thrown at src/PhpSpreadsheet/Worksheet/Worksheet.php:2596

        if ($beforeColumnIndex >= 1) {
            return $this->insertNewColumnBefore(Coordinate::stringFromColumnIndex($beforeColumnIndex), $numberOfColumns);
        }

        throw new Exception('Columns can only be inserted before at least column A (1).');
    }

    /**
     * Delete a row, updating all possible related data.
     *
     * @param int $row Remove rows, starting with this row number
     * @param int $numberOfRows Number of rows to remove
     *
     * @return $this
     */
    public function removeRow(int $row, int $numberOfRows = 1): static
    {
        if ($row < 1) {
            throw new Exception('Rows to be deleted should at least start from row 1.');
        }
        if ($numberOfRows === 0) {
            return $this;
        }
        if ($numberOfRows < 0) {
            $newRow = max(1, $row + $numberOfRows + 1);
            $numberOfRows = $row - $newRow + 1;
            $row = $newRow;
        }
        $newHighestRow = $this->cachedHighestRow;
        if ($newHighestRow >= $row) {
            $newHighestRow = max($row - 1, $this->cachedHighestRow - $numberOfRows);
        }
        $startRow = $row;
        $endRow = $startRow + $numberOfRows - 1;
        $removeKeys = [];
        $addKeys = [];
        foreach ($this->mergeCells as $key => $value) {

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Pass a start row >= 1; row 1 is the first spreadsheet row.
  2. To remove the first N rows including headers, use removeRow(1, $n).
  3. Clamp computed starts: $row = max(1, $computed);
  4. When using negative $numberOfRows (e.g. removeRow(5, -2)), verify the normalized start still lands at or above row 1.

Example fix

// before
$dataStart = 0; // 0-based header offset
$sheet->removeRow($dataStart, 1); // throws

// after
$sheet->removeRow(1, 1); // delete row 1 explicitly
// or delete rows 1-3 (header + 2 data rows):
$sheet->removeRow(1, 3);
Defensive patterns

Strategy: validation

Validate before calling

$row = $firstContentRow; // whatever the data says
if ($row < 1) {
    $row = 1;
}
$sheet->removeRow($row, 2);

Type guard

/** Spreadsheet rows are 1-based: row 1 is the first row. */
function isPositiveRow(int $row): bool
{
    return $row >= 1;
}

Prevention

When it happens

Trigger: removeRow(0) or removeRow(-2); header math like removeRow($firstDataRow - 1) evaluating to 0 when data starts at row 1; passing row numbers taken from 0-based arrays or parsed CSV offsets.

Common situations: Deleting everything above a data block but computing the start from a 0-based offset; importers feeding array indices to the API; using the negative-count convenience with a bad start row.

Related errors


AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17). Data as JSON: /api/errors/cffa6a62c2a01508. Report an issue: GitHub.