PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Exception

Rows can only be inserted before at least row 1.

Error message

Rows can only be inserted before at least row 1.

What it means

insertNewRowBefore(int $before, int $numberOfRows = 1) shifts existing rows down and inserts blanks before row $before via ReferenceHelper. Spreadsheet rows are 1-indexed, so the method requires $before >= 1 and throws immediately for 0 or negative values before any shifting work starts.

Source

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

        return $this;
    }

    /**
     * Insert a new row, updating all possible related data.
     *
     * @param int $before Insert before this row number
     * @param int $numberOfRows Number of new rows to insert
     *
     * @return $this
     */
    public function insertNewRowBefore(int $before, int $numberOfRows = 1): static
    {
        if ($before >= 1) {
            $objReferenceHelper = ReferenceHelper::getInstance();
            $objReferenceHelper->insertNewBefore('A' . $before, 0, $numberOfRows, $this);
        } else {
            throw new Exception('Rows can only be inserted before at least row 1.');
        }

        return $this;
    }

    /**
     * Insert a new column, updating all possible related data.
     *
     * @param string $before Insert before this column Name, eg: 'A'
     * @param int $numberOfColumns Number of new columns to insert
     *
     * @return $this
     */
    public function insertNewColumnBefore(string $before, int $numberOfColumns = 1): static
    {
        if (!is_numeric($before)) {
            $objReferenceHelper = ReferenceHelper::getInstance();
            $objReferenceHelper->insertNewBefore($before . '1', $numberOfColumns, 0, $this);

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Pass a row number >= 1; to insert above the first row use insertNewRowBefore(1).
  2. Convert 0-based positions at the boundary: insertNewRowBefore($zeroBasedIndex + 1, $count).
  3. Clamp computed values: $before = max(1, $computed);
  4. Audit loops that build row numbers from array keys; array_values($data) plus +1 removes the trap.

Example fix

// before
foreach ($data as $offset => $row) {
    $sheet->insertNewRowBefore($offset, 1); // $offset starts at 0 -> throws
}

// after
foreach (array_values($data) as $offset => $row) {
    $sheet->insertNewRowBefore($offset + 1, 1);
}
Defensive patterns

Strategy: validation

Validate before calling

$insertAt = count($templateRows) + 1; // spreadsheet rows are 1-based
$sheet->insertNewRowBefore(max(1, $insertAt), 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: insertNewRowBefore(0) or insertNewRowBefore(-3); a 0-based array index (foreach ($rows as $i => ...) with $i starting at 0) passed as the row number; offset arithmetic such as insertNewRowBefore($rowNumber - 1) evaluating to 0 when $rowNumber is 1.

Common situations: Classic zero-based vs one-base confusion when mapping PHP array positions to spreadsheet rows; importers computing an insertion point from CSV line numbers or database row offsets; refactors where a loop counter starting at 0 leaks into worksheet API calls.

Related errors


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