{"record":{"id":"0452fc7cb371a48b","repo":"PHPOffice/PhpSpreadsheet","slug":"only-little-endian-encoding-is-supported","errorCode":null,"errorMessage":"Only Little-Endian encoding is supported.","messagePattern":"Only Little-Endian encoding is supported\\.","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Reader\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/Shared/OLE.php","lineNumber":126,"sourceCode":"     *\n     * @return bool true on success, PEAR_Error on failure\n     */\n    public function read(string $filename): bool\n    {\n        $fh = @fopen($filename, 'rb');\n        if ($fh === false) {\n            throw new ReaderException(\"Can't open file $filename\");\n        }\n        $this->_file_handle = $fh;\n\n        $signature = fread($fh, 8);\n        if (\"\\xD0\\xCF\\x11\\xE0\\xA1\\xB1\\x1A\\xE1\" != $signature) {\n            throw new ReaderException(\"File doesn't seem to be an OLE container.\");\n        }\n        fseek($fh, 28);\n        if (fread($fh, 2) != \"\\xFE\\xFF\") {\n            // This shouldn't be a problem in practice\n            throw new ReaderException('Only Little-Endian encoding is supported.');\n        }\n        // Size of blocks and short blocks in bytes\n        /** @var int<1, max> */\n        $temp = 2 ** self::readInt2($fh);\n        $this->bigBlockSize = $temp;\n        $this->smallBlockSize = 2 ** self::readInt2($fh);\n\n        // Skip UID, revision number and version number\n        fseek($fh, 44);\n        // Number of blocks in Big Block Allocation Table\n        $bbatBlockCount = self::readInt4($fh);\n\n        // Root chain 1st block\n        $directoryFirstBlockId = self::readInt4($fh);\n\n        // Skip unused bytes\n        fseek($fh, 56);\n        // Streams shorter than this are stored using small blocks","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/Shared/OLE.php#L108-L144","documentation":"Thrown by Shared\\OLE::read() when the 2-byte field at offset 28 of the compound document is not the expected byte-order marker FE FF. The OLE2 spec defines only little-endian encoding, so a well-formed big-endian file is essentially nonexistent; in practice this exception marks a malformed or damaged header.","triggerScenarios":"A file that passed the 8-byte OLE signature but whose header bytes at offset 28-29 differ — typically random corruption, a file that begins with a coincidental OLE signature, or a truncated/patched container. Reached only after the signature check in OLE::read(), so it implies the file at least looks like an OLE document at byte 0.","commonSituations":"Bit-level corruption from bad transfers or disk faults; deliberately crafted files (fuzzing payloads) whose header is nonsense; files produced by broken non-Microsoft writers that emit partial OLE headers.","solutions":["Treat it as corruption: re-obtain the file from its source and retry; compare checksums if available.","Validate the file with another tool first (e.g. 'file report.xls', or opening in LibreOffice) to confirm the container is damaged.","Parse defensively: wrap legacy .xls parsing in try/catch and surface a 'corrupt file' message rather than a library stack trace.","If you accept untrusted uploads, reject files that fail signature+header validation early instead of parsing them."],"exampleFix":"// before\ntry {\n    $ole->read('invoice.xls');\n} catch (ReaderException $e) { /* generic */ }\n\n// after\ntry {\n    $ole->read('invoice.xls');\n} catch (ReaderException $e) {\n    if (str_contains($e->getMessage(), 'Only Little-Endian')) {\n        // header corruption — ask the sender for a fresh export\n        unlink('invoice.xls');\n        throw new RuntimeException('The .xls file is damaged. Please re-export it.', 0, $e);\n    }\n    throw $e;\n}","handlingStrategy":"try-catch","validationCode":"$head = (string) @file_get_contents($file, false, null, 28, 2);\nif ($head !== \"\\xFE\\xFF\") { /* header damaged — reject before OLE parsing */ }","typeGuard":null,"tryCatchPattern":"try { $ole->read($file); }\ncatch (\\PhpOffice\\PhpSpreadsheet\\Reader\\Exception $e) {\n    if (str_contains($e->getMessage(), 'Little-Endian')) {\n        throw new RuntimeException('Workbook container is damaged — please re-export.', 0, $e);\n    }\n    throw $e;\n}","preventionTips":["Treat this exception as proof of corruption; there is no big-endian OLE variant to support in practice.","Verify transfers with checksums when .xls files move between systems.","Validate the full header (signature + byte-order marker) before deeper parsing of untrusted files."],"tags":["ole","xls","file-corruption","phpspreadsheet"],"backgroundTag":"corrupted-file-header","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}