PHPOffice/PhpSpreadsheet · warning · PhpOffice\PhpSpreadsheet\Exception

Row $row is out of range ({$this->startRow} - {$this->endRow

Error message

Row $row is out of range ({$this->startRow} - {$this->endRow})

What it means

ColumnCellIterator::seek() validates the requested row against the iterator's startRow/endRow bounds (defaults derived from the highest row of the column when the iterator was constructed). Seeking outside that window throws with the valid interval in the message.

Source

Thrown at src/PhpSpreadsheet/Worksheet/ColumnCellIterator.php:100

    }

    /**
     * Set the row pointer to the selected row.
     *
     * @param int $row The row number to set the current pointer at
     *
     * @return $this
     */
    public function seek(int $row = 1): static
    {
        if (
            $this->onlyExistingCells
            && (!$this->cellCollection->has(Coordinate::stringFromColumnIndex($this->columnIndex) . $row))
        ) {
            throw new PhpSpreadsheetException('In "IterateOnlyExistingCells" mode and Cell does not exist');
        }
        if (($row < $this->startRow) || ($row > $this->endRow)) {
            throw new PhpSpreadsheetException("Row $row is out of range ({$this->startRow} - {$this->endRow})");
        }
        $this->currentRow = $row;

        return $this;
    }

    /**
     * Rewind the iterator to the starting row.
     */
    public function rewind(): void
    {
        $this->currentRow = $this->startRow;
    }

    /**
     * Return the current cell in this worksheet column.
     */
    public function current(): ?Cell

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Seek only within [$iterator->getStartRow()? use the boundaries] — obtain the current bounds or recreate the iterator with the needed range: new ColumnCellIterator($sheet, 'A', 1, 500)
  2. Validate $row against the sheet's highest row ([$sheet->getHighestRow()]) before seeking
  3. Iterate with foreach instead of seek() for sequential access

Example fix

// before
$cellIterator->seek(500);

// after
$row = min(max($row, $cellIterator->getStartRow()), $cellIterator->getEndRow());
$cellIterator->seek($row);
Defensive patterns

Strategy: validation

Validate before calling

$row = max($iterator->getStartRow(), min($row, $iterator->getEndRow()));
if ($row < $iterator->getStartRow() || $row > $iterator->getEndRow()) {
    throw new OutOfBoundsException("Row $row outside iterator bounds");
}
$iterator->seek($row);

Type guard

function rowWithinIteratorBounds(\PhpOffice\PhpSpreadsheet\Worksheet\ColumnCellIterator $it, int $row): bool
{
    return $row >= $it->getStartRow() && $row <= $it->getEndRow();
}

Try / catch

try {
    $iterator->seek($row);
} catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
    // recreate the iterator covering the requested row
    $iterator = new \PhpOffice\PhpSpreadsheet\Worksheet\ColumnCellIterator($sheet, $column, 1, $sheet->getHighestRow());
    $iterator->seek($row);
}

Prevention

When it happens

Trigger: $cellIterator->seek(500) when the iterator covers rows 1-100; seeking to a row below startRow, e.g. seek(1) after the iterator was created with a start row of 10.

Common situations: Random-access navigation on an iterator built for a narrower range; row numbers computed from user input or other sheets assumed to match this column's extent.

Related errors


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