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

File $filename does not exist

Error message

File $filename does not exist

What it means

While building [Content_Types].xml for Xlsx output, every drawing image must be mapped to a MIME type. ContentTypes::getImageMimeType() first matches a 'data:image/...;base64,' URI, then requires an existing readable file (File::fileExists); if neither holds it throws with the stored path. Typical cause: a Drawing whose image file was deleted, moved, or never existed by the time save() runs.

Solutions

  1. Keep image files on disk until after save() completes (unlink only after save returns)
  2. Copy user-supplied images to your own storage and point the Drawing at that copy
  3. Before save, iterate the sheet's drawings and verify is_file($drawing->getPath())
  4. Use a MemoryDrawing or a data: URI path so no external file is needed (the code explicitly supports data: URIs)

Example fix

// before
$drawing->setPath($uploadedTmpPath);
$writer->save('out.xlsx');
unlink($uploadedTmpPath); // risky if save is called again later

// after
$localCopy = storage_path('images/' . basename($uploadedTmpPath));
copy($uploadedTmpPath, $localCopy);
$drawing->setPath($localCopy);
$writer->save('out.xlsx');
Defensive patterns

Strategy: validation

Validate before calling

/** All drawing images must exist (or be data URIs) before Xlsx save. */
function drawingImagesExist(Spreadsheet $spreadsheet): bool
{
    foreach ($spreadsheet->getAllSheets() as $sheet) {
        foreach ($sheet->getDrawingCollection() as $drawing) {
            if ($drawing instanceof \PhpOffice\PhpSpreadsheet\Worksheet\Drawing) {
                $path = $drawing->getPath();
                if (!str_starts_with($path, 'data:') && !is_file($path)) {
                    return false;
                }
            }
        }
    }

    return true;
}

if (!drawingImagesExist($spreadsheet)) {
    throw new RuntimeException('A drawing references a missing image file; export aborted');
}

Try / catch

try {
    $writer->save('out.xlsx');
} catch (\PhpOffice\PhpSpreadsheet\Writer\Exception $e) {
    if (preg_match('/File (.+) does not exist/u', $e->getMessage(), $m)) {
        // $m[1] names the missing image; drop that drawing and retry once
        removeDrawingByPath($spreadsheet, $m[1]);
        $writer->save('out.xlsx');
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: $drawing = new Drawing(); $drawing->setPath($tmpPath); ... then unlink($tmpPath) or queue/session temp-file cleanup removes it before $writer->save('out.xlsx'); or a path with a typo/nonexistent location.

Common situations: Queued jobs that process uploads after PHP's tmp-dir cleanup; flows that save twice with a cleanup in between; images uploaded to object storage and deleted locally right after being referenced.

Related errors


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

Appendix: source

Thrown at src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php:294

    /**
     * Get image mime type.
     *
     * @param string $filename Filename
     *
     * @return string Mime Type
     */
    private function getImageMimeType(string $filename): string
    {
        if (Preg::isMatch('~^data:(image/[^;]+);base64,~', $filename, $matches)) {
            return $matches[1];
        }
        if (File::fileExists($filename)) {
            $image = getimagesize($filename);

            return image_type_to_mime_type((is_array($image) && count($image) >= self::$three) ? $image[2] : 0);
        }

        throw new WriterException("File $filename does not exist");
    }

    /**
     * Write Default content type.
     *
     * @param string $partName Part name
     * @param string $contentType Content type
     */
    private function writeDefaultContentType(XMLWriter $objWriter, string $partName, string $contentType): void
    {
        if ($partName != '' && $contentType != '') {
            // Write content type
            $objWriter->startElement('Default');
            $objWriter->writeAttribute('Extension', $partName);
            $objWriter->writeAttribute('ContentType', $contentType);
            $objWriter->endElement();
        } else {
            throw new WriterException('Invalid parameters passed.');

View on GitHub (pinned to 65b080eef4)