{"record":{"id":"f2a0e41d51004040","repo":"PHPOffice/PhpSpreadsheet","slug":"merge-can-only-be-removed-from-a-range-of-cells","errorCode":null,"errorMessage":"Merge can only be removed from a range of cells.","messagePattern":"Merge can only be removed from a range of cells\\.","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/Worksheet/Worksheet.php","lineNumber":1976,"sourceCode":"     *\n     * @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range A simple string containing a Cell range like 'A1:E10'\n     *              or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),\n     *              or an AddressRange.\n     *\n     * @return $this\n     */\n    public function unmergeCells(AddressRange|string|array $range): static\n    {\n        $range = Functions::trimSheetFromCellReference(Validations::validateCellRange($range));\n\n        if (str_contains($range, ':')) {\n            if (isset($this->mergeCells[$range])) {\n                unset($this->mergeCells[$range]);\n            } else {\n                throw new Exception('Cell range ' . $range . ' not known as merged.');\n            }\n        } else {\n            throw new Exception('Merge can only be removed from a range of cells.');\n        }\n\n        return $this;\n    }\n\n    /**\n     * Get merge cells array.\n     *\n     * @return string[]\n     */\n    public function getMergeCells(): array\n    {\n        return $this->mergeCells;\n    }\n\n    /**\n     * Set merge cells array for the entire sheet. Use instead mergeCells() to merge\n     * a single cell range.","sourceCodeStart":1958,"sourceCodeEnd":1994,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/Worksheet/Worksheet.php#L1958-L1994","documentation":"Worksheet::unmergeCells() requires the normalized range to contain ':' — merges are always stored as (at least) 'A1:B2' style ranges, so removing a merge identified by a single cell address is rejected before the map lookup. (mergeCells() auto-expands 'A1' to 'A1:A1', but unmergeCells() deliberately does not.)","triggerScenarios":"$sheet->unmergeCells('A1'); unmerging with a coordinate variable that holds a single address because the ':B2' part was lost; splitting a range string and passing only the first half.","commonSituations":"Application code that knows 'the merged cell A1' and tries to unmerge just it; generic 'clear formatting at X' routines that forward single coordinates to unmergeCells().","solutions":["Pass the full merged range: unmergeCells('A1:B2')","When only the cell is known, look up the containing merge in getMergeCells() and unmerge that exact string"],"exampleFix":"// before\n$sheet->unmergeCells('A1'); // single cell -> throws\n\n// after\nforeach ($sheet->getMergeCells() as $merge) {\n    [$from] = explode(':', $merge);\n    if ($from === 'A1') {\n        $sheet->unmergeCells($merge);\n    }\n}","handlingStrategy":"validation","validationCode":"if (str_contains($range, ':')) {\n    $sheet->unmergeCells($range);\n} else {\n    foreach ($sheet->getMergeCells() as $merge) {\n        if (str_starts_with($merge, $range . ':') || explode(':', $merge)[0] === $range) {\n            $sheet->unmergeCells($merge);\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat 'unmerge' as an operation on stored ranges, not on cells","Single-cell 'A1' style arguments belong to cell APIs, never unmergeCells","When in doubt, dump getMergeCells() and choose the entry programmatically"],"tags":["phpspreadsheet","unmerge-cells","merge","single-cell","range-required"],"backgroundTag":"invalid-range-format","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}