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
- Pass a start row >= 1; row 1 is the first spreadsheet row.
- To remove the first N rows including headers, use removeRow(1, $n).
- Clamp computed starts: $row = max(1, $computed);
- 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
- To delete from the top of the sheet, always start at removeRow(1, $n).
- Clamp computed start rows with max(1, $row).
- Remember negative $numberOfRows re-bases the start row; verify it stays >= 1.
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
- Rows can only be inserted before at least row 1.
- Freeze pane can not be set on a range of cells.
- Column references should not be numeric.
- Columns can only be inserted before at least column A (1).
- Columns to be deleted should at least start from column A (1
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/cffa6a62c2a01508.
Report an issue: GitHub.