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

Cannot read this Excel file. Version is too old.

Error message

Cannot read this Excel file. Version is too old.

What it means

When reading the workbook-globals substream of a BIFF file, the reader accepts only version 0x0600 (BIFF8, Excel 97+) or 0x0500 (BIFF7, Excel 95). Any lower BOF version — BIFF2/3/4 from Excel 2 through Excel 4 — triggers 'Cannot read this Excel file. Version is too old.' before any records are parsed.

Source

Thrown at src/PhpSpreadsheet/Reader/Xls.php:820

    /**
     * Read BOF.
     */
    protected function readBof(): void
    {
        $length = self::getUInt2d($this->data, $this->pos + 2);
        $recordData = substr($this->data, $this->pos + 4, $length);

        // move stream pointer to next record
        $this->pos += 4 + $length;

        // offset: 2; size: 2; type of the following data
        $substreamType = self::getUInt2d($recordData, 2);

        switch ($substreamType) {
            case self::XLS_WORKBOOKGLOBALS:
                $version = self::getUInt2d($recordData, 0);
                if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
                    throw new Exception('Cannot read this Excel file. Version is too old.');
                }
                $this->version = $version;

                break;
            case self::XLS_WORKSHEET:
                // do not use this version information for anything
                // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
                break;
            default:
                // substream, e.g. chart
                // just skip the entire substream
                do {
                    $code = self::getUInt2d($this->data, $this->pos);
                    $this->readDefault();
                } while ($code != self::XLS_TYPE_EOF && $this->pos < $this->dataSize);

                break;
        }

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Convert the file once via LibreOffice: soffice --headless --convert-to xlsx old.xls, then load the .xlsx with the Xlsx reader
  2. Or open in Excel and Save As a modern .xls (97-2003) or .xlsx
  3. For batch archives, script a pre-conversion step (LibreOffice headless) before PhpSpreadsheet ingestion

Example fix

// before
$spreadsheet = IOFactory::load('archive1993.xls'); // BIFF4 BOF -> 'Version is too old'

# after (shell)
soffice --headless --convert-to xlsx archive1993.xls
// then in PHP
$spreadsheet = IOFactory::load('archive1993.xlsx');
Defensive patterns

Strategy: fallback

Validate before calling

$fh = fopen($filename, 'rb');
$head = unpack('vid/vver', (string) fread($fh, 512)); // OLE header sanity
fclose($fh);
if (($head['ver'] ?? 0) < 0x0500) {
    // pre-BIFF7 file — convert before loading (see fallback)
}

Try / catch

try {
    $spreadsheet = IOFactory::load($path);
} catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
    if (str_contains($e->getMessage(), 'Version is too old')) {
        exec('soffice --headless --convert-to xlsx --outdir ' . escapeshellarg(sys_get_temp_dir()) . ' ' . escapeshellarg($path));
        $spreadsheet = IOFactory::load(preg_replace('/\.xls$/', '.xlsx', basename($path)), ); // converted copy
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Loading a genuine .xls created by Excel 4.0 or earlier (1992-1993 era), or a tool emitting BIFF2-4; historic archives; files whose substream version bytes were corrupted, masquerading as a prehistoric version.

Common situations: Digitization projects ingesting decades-old archives; .xls files produced by ancient SCADA/instrument export software; corrupted downloads that garble the BOF header.

Related errors


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