PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Exception

{filename} is an Invalid HTML file.

Error message

{filename} is an Invalid HTML file.

What it means

The Html reader's loadIntoExisting() guards itself with canRead(), which sniffs for recognizable HTML markup. When the check fails it throws 'Invalid HTML file' before DOM parsing starts — the content does not look like HTML to the reader's heuristic.

Source

Thrown at src/PhpSpreadsheet/Reader/Html.php:797

                }
                //    simply append the text if the cell content is a plain text string
                $cellContent .= $domText;
                //    but if we have a rich text run instead, we need to append it correctly
                //    TODO
            } elseif ($child instanceof DOMElement) {
                $this->processDomElementBody($sheet, $row, $column, $cellContent, $child);
            }
        }
    }

    /**
     * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
     */
    public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet): Spreadsheet
    {
        // Validate
        if (!$this->canRead($filename)) {
            throw new Exception($filename . ' is an Invalid HTML file.');
        }

        // Create a new DOM object
        $dom = new DOMDocument();

        // Reload the HTML file into the DOM object
        if (is_bool($this->suppressLoadWarnings)) {
            $useErrors = libxml_use_internal_errors($this->suppressLoadWarnings);
        } else {
            $useErrors = null;
        }

        try {
            $convert = $this->getSecurityScannerOrThrow()->scanFile($filename);
            $convert = static::replaceNonAsciiIfNeeded($convert);
            $loaded = ($convert === null) ? false : $dom->loadHTML($convert);
        } catch (Throwable $e) {
            $loaded = false;

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Use IOFactory::identify() and the matching reader instead of hardcoding Html
  2. If the file must be HTML, verify the leading bytes contain real markup (e.g. str_contains strtolower for '<html' or '<table') before loading
  3. Convert .mht to plain .html (open in a browser or mail client and re-save) before import

Example fix

// before
$spreadsheet = (new Html())->loadIntoExisting('export.mht', $spreadsheet); // MHT not recognized -> throws

// after
$type = IOFactory::identify($uploadPath);
$reader = IOFactory::createReader($type);
$spreadsheet = $reader->loadIntoExisting($uploadPath, $spreadsheet);
Defensive patterns

Strategy: validation

Validate before calling

$head = substr((string) file_get_contents($filename), 0, 2048);
if (!str_contains(strtolower($head), '<')) {
    throw new RuntimeException('File contains no HTML markup');
}
$spreadsheet = (new Html())->loadIntoExisting($filename, $spreadsheet);

Prevention

When it happens

Trigger: loadIntoExisting() on a file that is actually plain text, JSON, XML, or empty; Excel's 'Single File Web Page' (.mht) exports, which the HTML reader cannot recognize; files whose markup is buried after large non-markup preamble so the sniff misses it.

Common situations: Feeding scraped or API-returned content saved as .html; user uploads validated only by extension; importing mail-exported .mht files.

Related errors


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