PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Exception

Freeze pane can not be set on a range of cells.

Error message

Freeze pane can not be set on a range of cells.

What it means

Worksheet::freezePane() defines a freeze split at a single cell coordinate, with an optional second argument for the top-left cell of the scrollable pane. A frozen pane is a split point, not an area, so after validating the argument and trimming any sheet prefix the method rejects anything that Coordinate::coordinateIsRange() recognizes as a range (any string containing ':'). Throwing here stops writers from serializing pane XML that Excel would treat as corrupt.

Source

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

     * @param null|array{0: int, 1: int}|CellAddress|string $topLeftCell default position of the right bottom pane
     *            Coordinate of the cell as a string, eg: 'C5'; or as an array of [$columnIndex, $row] (e.g. [3, 5]),
     *            or a CellAddress object.
     *
     * @return $this
     */
    public function freezePane(null|CellAddress|string|array $coordinate, null|CellAddress|string|array $topLeftCell = null, bool $frozenSplit = false): static
    {
        $this->panes = [
            'bottomRight' => null,
            'bottomLeft' => null,
            'topRight' => null,
            'topLeft' => null,
        ];
        $cellAddress = ($coordinate !== null)
            ? Functions::trimSheetFromCellReference(Validations::validateCellAddress($coordinate))
            : null;
        if ($cellAddress !== null && Coordinate::coordinateIsRange($cellAddress)) {
            throw new Exception('Freeze pane can not be set on a range of cells.');
        }
        $topLeftCell = ($topLeftCell !== null)
            ? Functions::trimSheetFromCellReference(Validations::validateCellAddress($topLeftCell))
            : null;

        if ($cellAddress !== null && $topLeftCell === null) {
            $coordinate = Coordinate::coordinateFromString($cellAddress);
            $topLeftCell = $coordinate[0] . $coordinate[1];
        }

        $topLeftCell = "$topLeftCell";
        $this->paneTopLeftCell = $topLeftCell;

        $this->freezePane = $cellAddress;
        $this->topLeftCell = $topLeftCell;
        if ($cellAddress === null) {
            $this->paneState = '';
            $this->xSplit = $this->ySplit = 0;

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Pass a single cell: freezePane('A2') freezes the top row, freezePane('B1') freezes column A, freezePane('B2') freezes both.
  2. If you only have a range string, use its first cell as the split point: [$first] = explode(':', $range, 2); freezePane($first).
  3. Remember the second parameter is the scrollable pane's top-left cell (e.g. freezePane('B2', 'C3')), not the second bound of an area.
  4. Validate free-text input with Coordinate::coordinateIsRange() before calling any worksheet geometry method.

Example fix

// before
$used = $sheet->calculateWorksheetDimension(); // e.g. A1:F20
$sheet->freezePane($used); // throws: range

// after
$sheet->freezePane('A2'); // freeze the top row only
// keep rows 1-3 and columns A-B visible:
$sheet->freezePane('C4');
Defensive patterns

Strategy: validation

Validate before calling

use PhpOffice\PhpSpreadsheet\Cell\Coordinate;

$coord = 'A1:F10';
if (Coordinate::coordinateIsRange($coord)) {
    // a freeze pane is one split cell, not an area
    [$coord] = explode(':', $coord, 2);
}
$sheet->freezePane($coord);

Type guard

use PhpOffice\PhpSpreadsheet\Cell\Coordinate;

/** True only for a single relative cell address like 'C5'. */
function isSingleCellCoordinate(string $address): bool
{
    return $address !== ''
        && !str_contains($address, '$')
        && !Coordinate::coordinateIsRange($address);
}

Prevention

When it happens

Trigger: Calling $sheet->freezePane('A1:F10') or freezePane('B2:D5') with any colon-containing string; reusing a used-range string such as the result of calculateWorksheetDimension() (which returns something like 'A1:G20'); forwarding a user- or config-supplied 'range to freeze' verbatim.

Common situations: Developers confuse 'freeze the header area' with 'pass the header range' and pass 'A1:C1' instead of 'A2'. Also seen when migrating older PHPExcel code that reused one range variable for styling, autofilter and freezing, and when a UI accepts a range that is forwarded without normalization.

Related errors


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