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.

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)

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.