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
- Convert the file once via LibreOffice: soffice --headless --convert-to xlsx old.xls, then load the .xlsx with the Xlsx reader
- Or open in Excel and Save As a modern .xls (97-2003) or .xlsx
- 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
- Batch-convert pre-95 archives with LibreOffice headless during ingestion
- Keep a format-inventory step in migration pipelines so ancient versions are quarantined early
- Prefer .xlsx output for long-term archival of legacy spreadsheets
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
- XOr encryption not supported
- Unexpected file pass record length
- Unrecognized space type in tAttrSpace token
- Unrecognized attribute flag in tAttr token
- Unrecognized function in formula
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/3c0a58116256a042.
Report an issue: GitHub.