PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Exception
CellRange array length must be 2 or 4
Error message
CellRange array length must be 2 or 4
What it means
Thrown by Validations::validateCellRange() when the range is supplied as a PHP array whose count() is not 2 or 4. The accepted array forms are exactly [columnIndex, row] (single cell, values duplicated to from/to) and [fromColumnIndex, fromRow, toColumnIndex, toRow]; any other length cannot describe a rectangle.
Source
Thrown at src/PhpSpreadsheet/Worksheet/Validations.php:105
$addressRange = self::convertWholeRowColumn($addressRange);
return empty($worksheet) ? strtoupper($addressRange) : $worksheet . '!' . strtoupper($addressRange);
}
if (is_array($cellRange)) {
switch (count($cellRange)) {
case 4:
$from = [$cellRange[0], $cellRange[1]];
$to = [$cellRange[2], $cellRange[3]];
break;
case 2:
$from = [$cellRange[0], $cellRange[1]];
$to = [$cellRange[0], $cellRange[1]];
break;
default:
throw new SpreadsheetException('CellRange array length must be 2 or 4');
}
$cellRange = new CellRange(CellAddress::fromColumnRowArray($from), CellAddress::fromColumnRowArray($to));
}
return (string) $cellRange;
}
public static function definedNameToCoordinate(string $coordinate, Worksheet $worksheet): string
{
// Uppercase coordinate
$coordinate = strtoupper($coordinate);
// Eliminate leading equal sign
$testCoordinate = Preg::replace('/^=/', '', $coordinate);
$defined = $worksheet->getParentOrThrow()->getDefinedName($testCoordinate, $worksheet);
if ($defined !== null) {
if ($defined->getWorksheet() === $worksheet && !$defined->isFormula()) {
$coordinate = Preg::replace('/^=/', '', $defined->getValue());
}View on GitHub (pinned to 65b080eef4)
Solutions
- Use the 2-element [columnIndex, row] or 4-element [fromCol, fromRow, toCol, toRow] integer array form
- Or skip arrays entirely: pass a string 'A1:B2' or a CellRange/AddressRange object
- count($range) === 2 || count($range) === 4 before calling mergeCells()
Example fix
// before
$sheet->mergeCells([3, 5, 6]); // count 3 -> throws
// after
$sheet->mergeCells([3, 5, 6, 8]); // C5:F8
// or
$sheet->mergeCells('C5:F8'); Defensive patterns
Strategy: type-guard
Validate before calling
if (is_array($range) && !in_array(count($range), [2, 4], true)) {
throw new InvalidArgumentException('CellRange array must have exactly 2 or 4 elements');
}
$sheet->mergeCells($range); Type guard
/** @param mixed $range */
function isAcceptableRangeArgument($range): bool
{
if (is_string($range) || $range instanceof \PhpOffice\PhpSpreadsheet\Cell\AddressRange) {
return true;
}
return is_array($range) && in_array(count($range), [2, 4], true);
} Prevention
- Prefer the string 'A1:B2' or [fromCol, fromRow, toCol, toRow] forms consistently across the codebase
- Never build the array by pushing optional bounds — assign all four positions explicitly
- Document the 2-or-4 contract wherever you accept dynamic ranges
When it happens
Trigger: $sheet->mergeCells([1, 2, 3]) (count 3); passing a nested pair like [[1,2],[3,4]]; passing [] or a flat list with 5+ entries to mergeCells(), unmergeCells() or any other API routed through validateCellRange.
Common situations: Building ranges from dynamic data where one bound is missing/null so count() ends up odd; copy-pasting a 3-element [from, to, extra] array from application code; assuming nested coordinate pairs are supported.
Related errors
- Invalid R1C1-format Cell Reference
- Invalid A1-format Cell Reference
- Each array entry must be an array
- Not a cell range address
- Not a cell range address
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/7f7e00dfb757ceef.
Report an issue: GitHub.