PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Writer\Exception

No writer found for type $writerType

Error message

No writer found for type $writerType

What it means

IOFactory::createWriter() accepts either one of the registered short names ('Xls', 'Xlsx', 'Ods', 'Csv', 'Html', plus the PDF writers 'Tcpdf', 'Dompdf', 'Mpdf' and anything added via registerWriter()) or a full writer class-string. The lookup is case-sensitive: a string that is neither a registered value (a class name) nor a registered key throws immediately, before any file work starts.

Source

Thrown at src/PhpSpreadsheet/IOFactory.php:94

            self::WRITER_ODS => Writer\Ods::class,
            self::WRITER_CSV => Writer\Csv::class,
            self::WRITER_HTML => Writer\Html::class,
            'Tcpdf' => Writer\Pdf\Tcpdf::class,
            'Dompdf' => Writer\Pdf\Dompdf::class,
            'Mpdf' => Writer\Pdf\Mpdf::class,
        ];
    }

    /**
     * Create Writer\IWriter.
     */
    public static function createWriter(Spreadsheet $spreadsheet, string $writerType): IWriter
    {
        /** @var class-string<IWriter> */
        $className = $writerType;
        if (!in_array($writerType, self::$writers, true)) {
            if (!isset(self::$writers[$writerType])) {
                throw new Writer\Exception("No writer found for type $writerType");
            }

            // Instantiate writer
            $className = self::$writers[$writerType];
        }

        return new $className($spreadsheet);
    }

    /**
     * Create IReader.
     *
     * @param array<string, class-string<IReader>> $mergeArray
     *        Array to use to find reader, self::$readers will be used if $readers is empty.
     */
    public static function createReader(string $readerType, array $mergeArray = []): IReader
    {
        /** @var class-string<IReader> */

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Use the exact constants: IOFactory::WRITER_XLSX ('Xlsx'), WRITER_XLS ('Xls'), WRITER_CSV ('Csv'), WRITER_HTML ('Html'), WRITER_ODS ('Ods')
  2. Map PHPExcel-era names: 'Excel2007' -> 'Xlsx', 'Excel5' -> 'Xls', 'OOCalc' -> 'Ods'
  3. Normalize extension-derived strings with ucfirst() before passing, or pass a class-string like \PhpOffice\PhpSpreadsheet\Writer\Xlsx::class

Example fix

// before
$type = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); // 'xlsx'
$writer = IOFactory::createWriter($spreadsheet, $type);

// after
$type = ucfirst(strtolower(pathinfo($filename, PATHINFO_EXTENSION)));
$writer = IOFactory::createWriter($spreadsheet, $type); // 'Xlsx'
Defensive patterns

Strategy: validation

Validate before calling

const KNOWN_WRITERS = ['Xls', 'Xlsx', 'Ods', 'Csv', 'Html', 'Tcpdf', 'Dompdf', 'Mpdf'];

/** Accept user input, extension, or PHPExcel-era names; return a valid writer type. */
function normalizeWriterType(string $type): string
{
    $aliases = [
        'excel2007' => 'Xlsx', 'excel5' => 'Xls', 'oocalc' => 'Ods',
        'excel' => 'Xlsx', 'pdf' => 'Mpdf',
    ];
    $type = $aliases[strtolower(trim($type))] ?? ucfirst(strtolower(trim($type)));
    if (!in_array($type, KNOWN_WRITERS, true)
        && !is_a($type, \PhpOffice\PhpSpreadsheet\Writer\IWriter::class, true)) {
        throw new InvalidArgumentException("Unknown writer type: $type");
    }

    return $type;
}

$writer = IOFactory::createWriter($spreadsheet, normalizeWriterType($userType));

Prevention

When it happens

Trigger: IOFactory::createWriter($spreadsheet, 'xlsx') with lowercase; 'Excel2007'/'Excel5' (pre-1.0 PHPExcel names); any typo or stray whitespace like 'Xlsx '.

Common situations: Deriving the writer type from pathinfo() extension and passing it unnormalized; following old PHPExcel tutorials; case differences introduced during refactors.

Related errors


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