PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Exception

CellXf index is out of bounds.

Error message

CellXf index is out of bounds.

What it means

Spreadsheet::removeCellXfByIndex(int) deletes one entry from the shared cellXf style collection and then rewrites every cell whose xfIndex pointed above the removed slot (decrementing by one). The guard rejects an index greater than count-1 with 'CellXf index is out of bounds.' Removing styles by hand is low-level; most code should prune via garbageCollect().

Source

Thrown at src/PhpSpreadsheet/Spreadsheet.php:1336

    /**
     * Add a cellXf to the workbook.
     */
    public function addCellXf(Style $style): void
    {
        $this->cellXfCollection[] = $style;
        $style->setIndex(count($this->cellXfCollection) - 1);
    }

    /**
     * Remove cellXf by index. It is ensured that all cells get their xf index updated.
     *
     * @param int $cellStyleIndex Index to cellXf
     */
    public function removeCellXfByIndex(int $cellStyleIndex): void
    {
        if ($cellStyleIndex > count($this->cellXfCollection) - 1) {
            throw new Exception('CellXf index is out of bounds.');
        }

        // first remove the cellXf
        array_splice($this->cellXfCollection, $cellStyleIndex, 1);

        // then update cellXf indexes for cells
        foreach ($this->workSheetCollection as $worksheet) {
            foreach ($worksheet->getCoordinates(false) as $coordinate) {
                $cell = $worksheet->getCell($coordinate);
                $xfIndex = $cell->getXfIndex();
                if ($xfIndex > $cellStyleIndex) {
                    // decrease xf index by 1
                    $cell->setXfIndex($xfIndex - 1);
                } elseif ($xfIndex == $cellStyleIndex) {
                    // set to default xf index 0
                    $cell->setXfIndex(0);
                }
            }

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Validate first: if ($i >= 0 && $i < count($spreadsheet->getCellXfCollection())) removeCellXfByIndex($i);
  2. Prefer $spreadsheet->garbageCollect(), which removes unreferenced cellXfs and fixes cell indexes atomically.
  3. When removing several, iterate from the highest index downward.
  4. Re-read count($spreadsheet->getCellXfCollection()) at each step instead of caching it.

Example fix

// before
$spreadsheet->removeCellXfByIndex(count($spreadsheet->getCellXfCollection())); // off-by-one

// after
$spreadsheet->garbageCollect(); // prune all unused styles safely
// or, for one slot:
$count = count($spreadsheet->getCellXfCollection());
if ($i >= 0 && $i < $count) {
    $spreadsheet->removeCellXfByIndex($i);
}
Defensive patterns

Strategy: validation

Validate before calling

if ($cellStyleIndex >= 0 && $cellStyleIndex < count($spreadsheet->getCellXfCollection())) {
    $spreadsheet->removeCellXfByIndex($cellStyleIndex);
}

Type guard

function isValidCellXfIndex(\PhpOffice\PhpSpreadsheet\Spreadsheet $s, int $i): bool
{
    return $i >= 0 && $i < count($s->getCellXfCollection());
}

Try / catch

try {
    $spreadsheet->removeCellXfByIndex($i);
} catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
    // stale index — refresh and skip
    error_log('cellXf removal skipped: ' . $e->getMessage());
}

Prevention

When it happens

Trigger: Calling removeCellXfByIndex($i) with $i >= count($spreadsheet->getCellXfCollection()); looping 'for ($i = 0; $i <= $count; $i++)' (off-by-one); using an xfIndex captured before earlier removals shrank the collection.

Common situations: Hand-rolled style-deduplication or memory-optimization code that prunes the xf collection; indexes taken from cell getXfIndex() but applied to a different workbook whose collection is smaller; iterating a snapshot of the collection while deleting from it.

Related errors


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