PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Exception

Merge can only be removed from a range of cells.

Error message

Merge can only be removed from a range of cells.

What it means

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.)

Source

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

     *
     * @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'
     *              or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]),
     *              or an AddressRange.
     *
     * @return $this
     */
    public function unmergeCells(AddressRange|string|array $range): static
    {
        $range = Functions::trimSheetFromCellReference(Validations::validateCellRange($range));

        if (str_contains($range, ':')) {
            if (isset($this->mergeCells[$range])) {
                unset($this->mergeCells[$range]);
            } else {
                throw new Exception('Cell range ' . $range . ' not known as merged.');
            }
        } else {
            throw new Exception('Merge can only be removed from a range of cells.');
        }

        return $this;
    }

    /**
     * Get merge cells array.
     *
     * @return string[]
     */
    public function getMergeCells(): array
    {
        return $this->mergeCells;
    }

    /**
     * Set merge cells array for the entire sheet. Use instead mergeCells() to merge
     * a single cell range.

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Pass the full merged range: unmergeCells('A1:B2')
  2. When only the cell is known, look up the containing merge in getMergeCells() and unmerge that exact string

Example fix

// before
$sheet->unmergeCells('A1'); // single cell -> throws

// after
foreach ($sheet->getMergeCells() as $merge) {
    [$from] = explode(':', $merge);
    if ($from === 'A1') {
        $sheet->unmergeCells($merge);
    }
}
Defensive patterns

Strategy: validation

Validate before calling

if (str_contains($range, ':')) {
    $sheet->unmergeCells($range);
} else {
    foreach ($sheet->getMergeCells() as $merge) {
        if (str_starts_with($merge, $range . ':') || explode(':', $merge)[0] === $range) {
            $sheet->unmergeCells($merge);
        }
    }
}

Prevention

When it happens

Trigger: $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.

Common situations: 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().

Related errors


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