{"record":{"id":"5747f6e5bc4f442d","repo":"PHPOffice/PhpSpreadsheet","slug":"unexpected-file-pass-record-length","errorCode":null,"errorMessage":"Unexpected file pass record length","messagePattern":"Unexpected file pass record length","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Reader\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/Reader/Xls.php","lineNumber":868,"sourceCode":"     *\n     * This record is part of the File Protection Block. It\n     * contains information about the read/write password of the\n     * file. All record contents following this record will be\n     * encrypted.\n     *\n     * --    \"OpenOffice.org's Documentation of the Microsoft\n     *         Excel File Format\"\n     *\n     * The decryption functions and objects used from here on in\n     * are based on the source of Spreadsheet-ParseExcel:\n     * https://metacpan.org/release/Spreadsheet-ParseExcel\n     */\n    protected function readFilepass(): void\n    {\n        $length = self::getUInt2d($this->data, $this->pos + 2);\n\n        if ($length < 54) {\n            throw new Exception('Unexpected file pass record length');\n        }\n\n        $recordData = $this->readRecordData($this->data, $this->pos + 4, $length);\n\n        // move stream pointer to next record\n        $this->pos += 4 + $length;\n\n        if (substr($recordData, 0, 2) !== \"\\x01\\x00\" || substr($recordData, 4, 2) !== \"\\x01\\x00\") {\n            throw new Exception('Unsupported encryption algorithm');\n        }\n        if (!$this->verifyPassword($this->encryptionPassword, substr($recordData, 6, 16), substr($recordData, 22, 16), substr($recordData, 38, 16), $this->md5Ctxt)) {\n            throw new Exception('Decryption password incorrect');\n        }\n\n        $this->encryption = self::MS_BIFF_CRYPTO_RC4;\n\n        // Decryption required from the record after next onwards\n        $this->encryptionStartPos = $this->pos + self::getUInt2d($this->data, $this->pos + 2);","sourceCodeStart":850,"sourceCodeEnd":886,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/Reader/Xls.php#L850-L886","documentation":"Thrown while PhpSpreadsheet's Xls reader parses the FILEPASS record in the workbook-globals stream of a binary .xls (BIFF) file. FILEPASS marks the workbook as encrypted; the built-in RC4 decryptor expects at least 54 bytes of payload (2+2 version words, 16-byte document id, 16-byte salt, 16-byte hashed salt). A shorter record means the file uses a different encryption layout or is damaged.","triggerScenarios":"Loading an .xls saved with legacy XOR obfuscation (its FILEPASS payload is only 4 bytes), a FILEPASS record truncated by an interrupted download/transfer, or a file emitted by a non-Microsoft writer that lays out the record incorrectly.","commonSituations":"Password-protected files exported from old Excel 5/95-era ERP systems, Excel '97 files re-saved by LibreOffice with XOR encryption, corrupted email attachments, or files whose bytes were mangled by a text-mode FTP transfer.","solutions":["Open the file in Excel or LibreOffice, remove the password (File > Info > Protect Workbook / Save As without encryption) and load it again","Re-save the workbook as unencrypted .xlsx and read that instead","Verify the file actually opens in Excel; if not, it is corrupt and must be re-exported or repaired","Decrypt externally (e.g. LibreOffice headless convert or an MS-OFFICE-CRYPTO tool) and point the reader at the decrypted copy"],"exampleFix":"// before\n$spreadsheet = IOFactory::load('legacy-protected.xls'); // Unexpected file pass record length\n\n// after: strip the password with LibreOffice, then read the clear copy\nshell_exec('soffice --headless --convert-to xlsx --outdir /tmp legacy-protected.xls');\n$spreadsheet = IOFactory::load('/tmp/legacy-protected.xlsx');","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    $spreadsheet = $reader->load($path);\n} catch (\\PhpOffice\\PhpSpreadsheet\\Reader\\Exception $e) {\n    if (str_contains($e->getMessage(), 'Unexpected file pass record length')) {\n        // encrypted with an unsupported layout or corrupt: ask for a clean/re-saved file\n    }\n}","preventionTips":["Reject encrypted .xls uploads up front and request unencrypted or .xlsx files","Round-trip incoming legacy files through LibreOffice conversion as a normalization step","Test your pipeline once with a password-protected file so the failure path is known"],"tags":["xls","biff","encryption","filepass","phpspreadsheet"],"backgroundTag":"encrypted-file-unsupported","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}