{"record":{"id":"4c985b94f1e30a9a","repo":"PHPOffice/PhpSpreadsheet","slug":"expecting-8-byte-string","errorCode":null,"errorMessage":"Expecting 8 byte string","messagePattern":"Expecting 8 byte string","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Reader\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/Shared/OLE.php","lineNumber":540,"sourceCode":"            $res = pack('c', $hex) . $res;\n            $big_date = fmod($big_date, $factor);\n            $factor /= 256;\n        }\n\n        return $res;\n    }\n\n    /**\n     * Returns a timestamp from an OLE container's date.\n     *\n     * @param string $oleTimestamp A binary string with the encoded date\n     *\n     * @return float|int The Unix timestamp corresponding to the string\n     */\n    public static function OLE2LocalDate(string $oleTimestamp)\n    {\n        if (strlen($oleTimestamp) != 8) {\n            throw new ReaderException('Expecting 8 byte string');\n        }\n\n        // convert to units of 100 ns since 1601:\n        /** @var int[] */\n        $unpackedTimestamp = unpack('v4', $oleTimestamp) ?: [];\n        $timestampHigh = (float) $unpackedTimestamp[4] * 65536 + (float) $unpackedTimestamp[3];\n        $timestampLow = (float) $unpackedTimestamp[2] * 65536 + (float) $unpackedTimestamp[1];\n\n        // translate to seconds since 1601:\n        $timestampHigh /= 10000000;\n        $timestampLow /= 10000000;\n\n        // days from 1601 to 1970:\n        $days = 134774;\n\n        // translate to seconds since 1970:\n        $unixTimestamp = floor(65536.0 * 65536.0 * $timestampHigh + $timestampLow - $days * 24 * 3600 + 0.5);\n","sourceCodeStart":522,"sourceCodeEnd":558,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/Shared/OLE.php#L522-L558","documentation":"Thrown by OLE::OLE2LocalDate() when the binary timestamp it was handed is not exactly 8 bytes. OLE property timestamps are 64-bit values (100ns ticks since 1601) stored as 8 raw bytes; a different length means the caller sliced the property block incorrectly or the stream is truncated.","triggerScenarios":"Reader\\Xls calls OLE::OLE2LocalDate(substr($this->summaryInformation, $offset, 8)) when parsing document properties; a summary-information sector shorter than expected (truncated file) yields fewer than 8 bytes and the strlen() != 8 check throws. Direct calls with a hand-built string of the wrong length also trigger it.","commonSituations":".xls files with missing/incomplete SummaryInformation streams (common in files from old ERP exports or files cut off mid-write); corrupted uploads; tools that rewrite summary streams incorrectly.","solutions":["Retry with a freshly exported copy of the workbook; confirm the file opens in Excel/LibreOffice.","Wrap property-heavy loads in try/catch and, if only metadata is affected, fall back to loading with readDocumentProperties disabled (the Xls reader option) so the damaged summary stream is not parsed.","Reject or quarantine uploads that fail structural checks (size, signature) to keep malformed files out of the parser.","Keep PhpSpreadsheet updated, since robustness fixes for truncated property streams land in releases."],"exampleFix":"// before\n$reader = new \\PhpOffice\\PhpSpreadsheet\\Reader\\Xls();\n$spreadsheet = $reader->load('legacy.xls'); // Expecting 8 byte string\n\n// after\n$reader = new \\PhpOffice\\PhpSpreadsheet\\Reader\\Xls();\n$reader->setReadDocumentProperties(false); // skip damaged summary stream\n$spreadsheet = $reader->load('legacy.xls');","handlingStrategy":"fallback","validationCode":"// No reliable pre-check for damaged summary streams; disable property parsing when\n// a file is known to come from a flaky source:\n$reader = new \\PhpOffice\\PhpSpreadsheet\\Reader\\Xls();\n$reader->setReadDocumentProperties(false);","typeGuard":null,"tryCatchPattern":"try { $spreadsheet = $reader->load($file); }\ncatch (\\PhpOffice\\PhpSpreadsheet\\Reader\\Exception $e) {\n    if (str_contains($e->getMessage(), 'Expecting 8 byte string')) {\n        $reader->setReadDocumentProperties(false);\n        $spreadsheet = $reader->load($file); // retry without metadata\n    } else { throw $e; }\n}","preventionTips":["Load with setReadDocumentProperties(false) when you only need cell data — it skips the fragile summary streams entirely.","Quarantine files whose property streams are truncated; they often have more damage inside."],"tags":["ole","metadata","file-corruption","phpspreadsheet"],"backgroundTag":"corrupted-binary-structure","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}