{"record":{"id":"3c0a58116256a042","repo":"PHPOffice/PhpSpreadsheet","slug":"cannot-read-this-excel-file-version-is-too-old","errorCode":null,"errorMessage":"Cannot read this Excel file. Version is too old.","messagePattern":"Cannot read this Excel file\\. Version is too old\\.","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Reader\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/Reader/Xls.php","lineNumber":820,"sourceCode":"    /**\n     * Read BOF.\n     */\n    protected function readBof(): void\n    {\n        $length = self::getUInt2d($this->data, $this->pos + 2);\n        $recordData = substr($this->data, $this->pos + 4, $length);\n\n        // move stream pointer to next record\n        $this->pos += 4 + $length;\n\n        // offset: 2; size: 2; type of the following data\n        $substreamType = self::getUInt2d($recordData, 2);\n\n        switch ($substreamType) {\n            case self::XLS_WORKBOOKGLOBALS:\n                $version = self::getUInt2d($recordData, 0);\n                if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {\n                    throw new Exception('Cannot read this Excel file. Version is too old.');\n                }\n                $this->version = $version;\n\n                break;\n            case self::XLS_WORKSHEET:\n                // do not use this version information for anything\n                // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream\n                break;\n            default:\n                // substream, e.g. chart\n                // just skip the entire substream\n                do {\n                    $code = self::getUInt2d($this->data, $this->pos);\n                    $this->readDefault();\n                } while ($code != self::XLS_TYPE_EOF && $this->pos < $this->dataSize);\n\n                break;\n        }","sourceCodeStart":802,"sourceCodeEnd":838,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/Reader/Xls.php#L802-L838","documentation":"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.","triggerScenarios":"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.","commonSituations":"Digitization projects ingesting decades-old archives; .xls files produced by ancient SCADA/instrument export software; corrupted downloads that garble the BOF header.","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"],"exampleFix":"// before\n$spreadsheet = IOFactory::load('archive1993.xls'); // BIFF4 BOF -> 'Version is too old'\n\n# after (shell)\nsoffice --headless --convert-to xlsx archive1993.xls\n// then in PHP\n$spreadsheet = IOFactory::load('archive1993.xlsx');","handlingStrategy":"fallback","validationCode":"$fh = fopen($filename, 'rb');\n$head = unpack('vid/vver', (string) fread($fh, 512)); // OLE header sanity\nfclose($fh);\nif (($head['ver'] ?? 0) < 0x0500) {\n    // pre-BIFF7 file — convert before loading (see fallback)\n}","typeGuard":null,"tryCatchPattern":"try {\n    $spreadsheet = IOFactory::load($path);\n} catch (\\PhpOffice\\PhpSpreadsheet\\Exception $e) {\n    if (str_contains($e->getMessage(), 'Version is too old')) {\n        exec('soffice --headless --convert-to xlsx --outdir ' . escapeshellarg(sys_get_temp_dir()) . ' ' . escapeshellarg($path));\n        $spreadsheet = IOFactory::load(preg_replace('/\\.xls$/', '.xlsx', basename($path)), ); // converted copy\n    } else {\n        throw $e;\n    }\n}","preventionTips":["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"],"tags":["xls","biff","legacy-format","file-version","unsupported-feature"],"backgroundTag":"unsupported-file-version","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}