{"record":{"id":"a3db1d50109bce32","repo":"PHPOffice/PhpSpreadsheet","slug":"freeze-pane-can-not-be-set-on-a-range-of-cells","errorCode":null,"errorMessage":"Freeze pane can not be set on a range of cells.","messagePattern":"Freeze pane can not be set on a range of cells\\.","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/Worksheet/Worksheet.php","lineNumber":2364,"sourceCode":"     * @param null|array{0: int, 1: int}|CellAddress|string $topLeftCell default position of the right bottom pane\n     *            Coordinate of the cell as a string, eg: 'C5'; or as an array of [$columnIndex, $row] (e.g. [3, 5]),\n     *            or a CellAddress object.\n     *\n     * @return $this\n     */\n    public function freezePane(null|CellAddress|string|array $coordinate, null|CellAddress|string|array $topLeftCell = null, bool $frozenSplit = false): static\n    {\n        $this->panes = [\n            'bottomRight' => null,\n            'bottomLeft' => null,\n            'topRight' => null,\n            'topLeft' => null,\n        ];\n        $cellAddress = ($coordinate !== null)\n            ? Functions::trimSheetFromCellReference(Validations::validateCellAddress($coordinate))\n            : null;\n        if ($cellAddress !== null && Coordinate::coordinateIsRange($cellAddress)) {\n            throw new Exception('Freeze pane can not be set on a range of cells.');\n        }\n        $topLeftCell = ($topLeftCell !== null)\n            ? Functions::trimSheetFromCellReference(Validations::validateCellAddress($topLeftCell))\n            : null;\n\n        if ($cellAddress !== null && $topLeftCell === null) {\n            $coordinate = Coordinate::coordinateFromString($cellAddress);\n            $topLeftCell = $coordinate[0] . $coordinate[1];\n        }\n\n        $topLeftCell = \"$topLeftCell\";\n        $this->paneTopLeftCell = $topLeftCell;\n\n        $this->freezePane = $cellAddress;\n        $this->topLeftCell = $topLeftCell;\n        if ($cellAddress === null) {\n            $this->paneState = '';\n            $this->xSplit = $this->ySplit = 0;","sourceCodeStart":2346,"sourceCodeEnd":2382,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/Worksheet/Worksheet.php#L2346-L2382","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass a single cell: freezePane('A2') freezes the top row, freezePane('B1') freezes column A, freezePane('B2') freezes both.","If you only have a range string, use its first cell as the split point: [$first] = explode(':', $range, 2); freezePane($first).","Remember the second parameter is the scrollable pane's top-left cell (e.g. freezePane('B2', 'C3')), not the second bound of an area.","Validate free-text input with Coordinate::coordinateIsRange() before calling any worksheet geometry method."],"exampleFix":"// before\n$used = $sheet->calculateWorksheetDimension(); // e.g. A1:F20\n$sheet->freezePane($used); // throws: range\n\n// after\n$sheet->freezePane('A2'); // freeze the top row only\n// keep rows 1-3 and columns A-B visible:\n$sheet->freezePane('C4');","handlingStrategy":"validation","validationCode":"use PhpOffice\\PhpSpreadsheet\\Cell\\Coordinate;\n\n$coord = 'A1:F10';\nif (Coordinate::coordinateIsRange($coord)) {\n    // a freeze pane is one split cell, not an area\n    [$coord] = explode(':', $coord, 2);\n}\n$sheet->freezePane($coord);","typeGuard":"use PhpOffice\\PhpSpreadsheet\\Cell\\Coordinate;\n\n/** True only for a single relative cell address like 'C5'. */\nfunction isSingleCellCoordinate(string $address): bool\n{\n    return $address !== ''\n        && !str_contains($address, '$')\n        && !Coordinate::coordinateIsRange($address);\n}","tryCatchPattern":null,"preventionTips":["Never feed calculateWorksheetDimension() or any 'A1:B2' string into freezePane(); it takes exactly one cell.","Treat the first argument as 'the first unfrozen cell': 'B2' keeps row 1 and column A visible.","Validate free-text config or user input with Coordinate::coordinateIsRange() before passing it to worksheet geometry methods."],"tags":["phpspreadsheet","worksheet","freeze-pane","cell-reference","argument-validation"],"backgroundTag":"invalid-cell-reference","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}