PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Exception
Merge must be on a valid range of cells.
Error message
Merge must be on a valid range of cells.
What it means
Worksheet::mergeCells() normalizes its input (sheet-name prefix trimmed, single cell expanded to 'X:Y') and then requires the result to match ^([A-Z]+)(\d+):([A-Z]+)(\d+)$ — uppercase letters, then digits, on both sides. Anything else (lowercase column letters, non-coordinate text, malformed addresses) fails the regex and throws.
Source
Thrown at src/PhpSpreadsheet/Worksheet/Worksheet.php:1851
* or an AddressRange.
* @param string $behaviour How the merged cells should behave.
* Possible values are:
* MERGE_CELL_CONTENT_EMPTY - Empty the content of the hidden cells
* MERGE_CELL_CONTENT_HIDE - Keep the content of the hidden cells
* MERGE_CELL_CONTENT_MERGE - Move the content of the hidden cells into the first cell
*
* @return $this
*/
public function mergeCells(AddressRange|string|array $range, string $behaviour = self::MERGE_CELL_CONTENT_EMPTY): static
{
$range = Functions::trimSheetFromCellReference(Validations::validateCellRange($range));
if (!str_contains($range, ':')) {
$range .= ":{$range}";
}
if (!Preg::isMatch('/^([A-Z]+)(\d+):([A-Z]+)(\d+)$/', $range, $matches)) {
throw new Exception('Merge must be on a valid range of cells.');
}
$this->mergeCells[$range] = $range;
$firstRow = (int) $matches[2];
$lastRow = (int) $matches[4];
$firstColumn = $matches[1];
$lastColumn = $matches[3];
$firstColumnIndex = Coordinate::columnIndexFromString($firstColumn);
$lastColumnIndex = Coordinate::columnIndexFromString($lastColumn);
$numberRows = $lastRow - $firstRow;
$numberColumns = $lastColumnIndex - $firstColumnIndex;
if ($numberRows === 1 && $numberColumns === 1) {
return $this;
}
// create upper left cell if it does not already exist
$upperLeft = "{$firstColumn}{$firstRow}";View on GitHub (pinned to 65b080eef4)
Solutions
- Normalize strings before merging: $range = strtoupper(trim($range)); then mergeCells($range)
- Build the range from trusted parts: Coordinate::stringFromColumnIndex($c) . $row . ':' . ...
- Prefer the structured forms — mergeCells([fromCol, fromRow, toCol, toRow]) or a CellRange — which are validated/normalized for you
Example fix
// before
$sheet->mergeCells('c5:f8'); // lowercase -> regex fails
// after
$sheet->mergeCells(strtoupper('c5:f8')); // 'C5:F8'
// or structured:
$sheet->mergeCells([3, 5, 6, 8]); Defensive patterns
Strategy: validation
Validate before calling
$range = strtoupper(trim($range));
if (preg_match('/^([A-Z]+)(\d+):([A-Z]+)(\d+)$/', $range) === 1) {
$sheet->mergeCells($range);
} else {
throw new InvalidArgumentException("Invalid merge range '$range'");
} Prevention
- Uppercase all incoming range strings — the merge regex is case-sensitive
- Build ranges from numeric parts via Coordinate::stringFromColumnIndex() instead of string surgery
- Prefer array [fromCol, fromRow, toCol, toRow] or CellRange inputs for dynamic merges
When it happens
Trigger: $sheet->mergeCells('a1:b2') — lowercase letters fail the regex; mergeCells('total'); mergeCells('A-1:B2'); values concatenated with stray characters ('A1 :B2'). The array/CellRange forms are normalized by Validations, so this mainly bites string input.
Common situations: Passing user-typed or external-system range strings directly (often lowercase); ranges assembled from unchecked variables where a piece is empty or non-numeric.
Related errors
- Unsupported binary comparison operator
- Cloning the calculation engine is not allowed!
- Unsupported numeric binary operation
- Locale file not found
- #VALUE!
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/0ace3a7d1dea9510.
Report an issue: GitHub.