{"record":{"id":"43eb2428661ef36f","repo":"PHPOffice/PhpSpreadsheet","slug":"cellxf-index-is-out-of-bounds","errorCode":null,"errorMessage":"CellXf index is out of bounds.","messagePattern":"CellXf index is out of bounds\\.","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/Spreadsheet.php","lineNumber":1336,"sourceCode":"\n    /**\n     * Add a cellXf to the workbook.\n     */\n    public function addCellXf(Style $style): void\n    {\n        $this->cellXfCollection[] = $style;\n        $style->setIndex(count($this->cellXfCollection) - 1);\n    }\n\n    /**\n     * Remove cellXf by index. It is ensured that all cells get their xf index updated.\n     *\n     * @param int $cellStyleIndex Index to cellXf\n     */\n    public function removeCellXfByIndex(int $cellStyleIndex): void\n    {\n        if ($cellStyleIndex > count($this->cellXfCollection) - 1) {\n            throw new Exception('CellXf index is out of bounds.');\n        }\n\n        // first remove the cellXf\n        array_splice($this->cellXfCollection, $cellStyleIndex, 1);\n\n        // then update cellXf indexes for cells\n        foreach ($this->workSheetCollection as $worksheet) {\n            foreach ($worksheet->getCoordinates(false) as $coordinate) {\n                $cell = $worksheet->getCell($coordinate);\n                $xfIndex = $cell->getXfIndex();\n                if ($xfIndex > $cellStyleIndex) {\n                    // decrease xf index by 1\n                    $cell->setXfIndex($xfIndex - 1);\n                } elseif ($xfIndex == $cellStyleIndex) {\n                    // set to default xf index 0\n                    $cell->setXfIndex(0);\n                }\n            }","sourceCodeStart":1318,"sourceCodeEnd":1354,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/Spreadsheet.php#L1318-L1354","documentation":"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().","triggerScenarios":"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.","commonSituations":"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.","solutions":["Validate first: if ($i >= 0 && $i < count($spreadsheet->getCellXfCollection())) removeCellXfByIndex($i);","Prefer $spreadsheet->garbageCollect(), which removes unreferenced cellXfs and fixes cell indexes atomically.","When removing several, iterate from the highest index downward.","Re-read count($spreadsheet->getCellXfCollection()) at each step instead of caching it."],"exampleFix":"// before\n$spreadsheet->removeCellXfByIndex(count($spreadsheet->getCellXfCollection())); // off-by-one\n\n// after\n$spreadsheet->garbageCollect(); // prune all unused styles safely\n// or, for one slot:\n$count = count($spreadsheet->getCellXfCollection());\nif ($i >= 0 && $i < $count) {\n    $spreadsheet->removeCellXfByIndex($i);\n}","handlingStrategy":"validation","validationCode":"if ($cellStyleIndex >= 0 && $cellStyleIndex < count($spreadsheet->getCellXfCollection())) {\n    $spreadsheet->removeCellXfByIndex($cellStyleIndex);\n}","typeGuard":"function isValidCellXfIndex(\\PhpOffice\\PhpSpreadsheet\\Spreadsheet $s, int $i): bool\n{\n    return $i >= 0 && $i < count($s->getCellXfCollection());\n}","tryCatchPattern":"try {\n    $spreadsheet->removeCellXfByIndex($i);\n} catch (\\PhpOffice\\PhpSpreadsheet\\Exception $e) {\n    // stale index — refresh and skip\n    error_log('cellXf removal skipped: ' . $e->getMessage());\n}","preventionTips":["Prefer garbageCollect() over manual xf pruning.","Remove from the highest index downward when pruning several.","Re-read count(getCellXfCollection()) each iteration."],"tags":["styles","index-out-of-bounds","cellxf","phpspreadsheet"],"backgroundTag":"index-out-of-bounds","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}