PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Exception

Workbook already contains a worksheet named '{$worksheet->ge

Error message

Workbook already contains a worksheet named '{$worksheet->getTitle()}'. Rename the external sheet first.

What it means

Spreadsheet::addExternalSheet() moves a worksheet that belongs to another workbook into this one, copying the external workbook's shared cellXf styles along with it. Unlike addSheet() there is no auto-rename path: if this workbook already has a sheet with the incoming title, it throws immediately and tells you to rename the external sheet first.

Source

Thrown at src/PhpSpreadsheet/Spreadsheet.php:871

        $returnValue = [];
        $worksheetCount = $this->getSheetCount();
        for ($i = 0; $i < $worksheetCount; ++$i) {
            $returnValue[] = $this->getSheet($i)->getTitle();
        }

        return $returnValue;
    }

    /**
     * Add external sheet.
     *
     * @param Worksheet $worksheet External sheet to add
     * @param null|int $sheetIndex Index where sheet should go (0,1,..., or null for last)
     */
    public function addExternalSheet(Worksheet $worksheet, ?int $sheetIndex = null): Worksheet
    {
        if ($this->sheetNameExists($worksheet->getTitle())) {
            throw new Exception("Workbook already contains a worksheet named '{$worksheet->getTitle()}'. Rename the external sheet first.");
        }

        // count how many cellXfs there are in this workbook currently, we will need this below
        $countCellXfs = count($this->cellXfCollection);

        // copy all the shared cellXfs from the external workbook and append them to the current
        foreach ($worksheet->getParentOrThrow()->getCellXfCollection() as $cellXf) {
            $this->addCellXf(clone $cellXf);
        }

        // move sheet to this workbook
        $worksheet->rebindParent($this);

        // update the cellXfs
        foreach ($worksheet->getCoordinates(false) as $coordinate) {
            $cell = $worksheet->getCell($coordinate);
            $cell->setXfIndex($cell->getXfIndex() + $countCellXfs);
        }

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Rename before moving: $sheet->setTitle('Source - ' . $sheet->getTitle()); then addExternalSheet($sheet).
  2. Or rename the collision target in the destination workbook to free the name.
  3. Or drop the existing destination sheet first: $target->removeSheetByIndex($target->getIndex($target->getSheetByName($name)));
  4. Unify names with a prefix/suffix scheme (source file basename, date) so merges never collide.

Example fix

// before
$target->addExternalSheet($source->getSheetByNameOrThrow('Sheet1')); // throws

// after
$sheet = $source->getSheetByNameOrThrow('Sheet1');
if ($target->sheetNameExists($sheet->getTitle())) {
    $sheet->setTitle(pathinfo($sourcePath, PATHINFO_FILENAME) . ' - ' . $sheet->getTitle());
}
$target->addExternalSheet($sheet);
Defensive patterns

Strategy: validation

Validate before calling

if ($target->sheetNameExists($sheet->getTitle())) {
    $sheet->setTitle($prefix . ' - ' . $sheet->getTitle());
}
$target->addExternalSheet($sheet);

Type guard

function externalSheetTitleIsFree(\PhpOffice\PhpSpreadsheet\Spreadsheet $target, \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $w): bool
{
    return !$target->sheetNameExists($w->getTitle());
}

Try / catch

try {
    $target->addExternalSheet($sheet);
} catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
    $sheet->setTitle(uniqid('sheet-'));
    $target->addExternalSheet($sheet);
}

Prevention

When it happens

Trigger: Merging two workbooks whose sheets share names (both contain 'Sheet1'): $target->addExternalSheet($source->getSheetByNameOrThrow('Sheet1')); importing a generated 'Data' tab into a report that already has 'Data'; consolidating monthly files that all use the same tab name.

Common situations: Report consolidation pipelines (many source files, one destination) without a per-source prefix/suffix on tab names; combining a template workbook with uploaded workbooks where users keep default names; merging after copying sheets with Worksheet::copy().

Related errors


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