PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Exception

You tried to remove a sheet by the out of bounds index: {$sh

Error message

You tried to remove a sheet by the out of bounds index: {$sheetIndex}. The actual number of sheets is {$numSheets}.

What it means

Spreadsheet::removeSheetByIndex() validates the requested index against count(workSheetCollection). The guard only catches indices greater than count-1; any index within 0..count-1 is passed to array_splice(). The message reports both the requested index and the real sheet count so you can see the mismatch. Note the check is an upper-bound check only.

Source

Thrown at src/PhpSpreadsheet/Spreadsheet.php:675

        }

        if ($worksheet->getParent() === null) {
            $worksheet->rebindParent($this);
        }

        return $worksheet;
    }

    /**
     * Remove sheet by index.
     *
     * @param int $sheetIndex Index position of the worksheet to remove
     */
    public function removeSheetByIndex(int $sheetIndex): void
    {
        $numSheets = count($this->workSheetCollection);
        if ($sheetIndex > $numSheets - 1) {
            throw new Exception(
                "You tried to remove a sheet by the out of bounds index: {$sheetIndex}. The actual number of sheets is {$numSheets}."
            );
        }
        array_splice($this->workSheetCollection, $sheetIndex, 1);

        // Adjust active sheet index if necessary
        if (
            ($this->activeSheetIndex >= $sheetIndex)
            && ($this->activeSheetIndex > 0 || $numSheets <= 1)
        ) {
            --$this->activeSheetIndex;
        }
    }

    /**
     * Get sheet by index.
     *
     * @param int $sheetIndex Sheet index

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Validate first: if ($i < 0 || $i >= $spreadsheet->getSheetCount()) abort; only then removeSheetByIndex($i).
  2. When removing several sheets by index, iterate from the highest index down so earlier removals do not shift later indices.
  3. If the user supplies a 1-based number, subtract 1 before calling the 0-based API.
  4. Prefer removing by name via getSheetByName()/getIndex() when the target is identified by title.

Example fix

// before
$spreadsheet->removeSheetByIndex(3); // throws when only 3 sheets (0..2)

// after
$index = 3;
if ($index < $spreadsheet->getSheetCount()) {
    $spreadsheet->removeSheetByIndex($index);
}
Defensive patterns

Strategy: validation

Validate before calling

if ($sheetIndex >= 0 && $sheetIndex < $spreadsheet->getSheetCount()) {
    $spreadsheet->removeSheetByIndex($sheetIndex);
} else {
    throw new \InvalidArgumentException("Invalid sheet index $sheetIndex");
}

Type guard

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

Try / catch

try {
    $spreadsheet->removeSheetByIndex($i);
} catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
    // index stale after earlier removals — re-count and skip
    error_log('Skipping removal, sheet already gone: ' . $e->getMessage());
}

Prevention

When it happens

Trigger: removeSheetByIndex($spreadsheet->getSheetCount()) (off-by-one: valid indices are 0..count-1); a hard-coded index like removeSheetByIndex(3) on a workbook that has fewer sheets; looping over a count captured before earlier removals shrank the collection.

Common situations: Import/merge scripts that drop 'raw data' sheets by position after processing; user input used as a sheet number (1-based from a UI vs 0-based API); deleting several sheets in a loop without re-counting.

Related errors


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