PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Reader\Exception
File doesn't seem to be an OLE container.
Error message
File doesn't seem to be an OLE container.
What it means
Thrown by Shared\OLE::read() when the first 8 bytes of the file are not the OLE2 compound-document signature \xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1. That signature prefixes legacy Microsoft binary formats (.xls, .doc, .ppt, .msi); anything else — ZIP-based .xlsx, CSV, HTML, plain text — is rejected immediately.
Source
Thrown at src/PhpSpreadsheet/Shared/OLE.php:121
/**
* Reads an OLE container from the contents of the file given.
*
* @acces public
*
* @return bool true on success, PEAR_Error on failure
*/
public function read(string $filename): bool
{
$fh = @fopen($filename, 'rb');
if ($fh === false) {
throw new ReaderException("Can't open file $filename");
}
$this->_file_handle = $fh;
$signature = fread($fh, 8);
if ("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" != $signature) {
throw new ReaderException("File doesn't seem to be an OLE container.");
}
fseek($fh, 28);
if (fread($fh, 2) != "\xFE\xFF") {
// This shouldn't be a problem in practice
throw new ReaderException('Only Little-Endian encoding is supported.');
}
// Size of blocks and short blocks in bytes
/** @var int<1, max> */
$temp = 2 ** self::readInt2($fh);
$this->bigBlockSize = $temp;
$this->smallBlockSize = 2 ** self::readInt2($fh);
// Skip UID, revision number and version number
fseek($fh, 44);
// Number of blocks in Big Block Allocation Table
$bbatBlockCount = self::readInt4($fh);
// Root chain 1st blockView on GitHub (pinned to 65b080eef4)
Solutions
- Detect the real format before parsing: use IOFactory::createReaderForFile($file) (it sniffs content) or IOFactory::load($file), instead of hardcoding the Xls/OLE reader.
- Sniff the magic bytes yourself: an .xls must start with D0 CF 11 E0; a ZIP header ('PK') means OOXML (.xlsx), so route accordingly.
- Re-export or re-download the source file if the header is corrupted; validate uploads by content, not by extension.
- If you expect CSV input, parse it with the Csv reader explicitly instead of the Xls path.
Example fix
// before
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
$spreadsheet = $reader->load('upload.xls'); // File doesn't seem to be an OLE container.
// after
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load('upload.xls');
// or route on the actual signature:
$sig = bin2hex((string) fread(fopen('upload.xls', 'rb'), 8));
$reader = ($sig === 'd0cf11e0a1b11ae1') ? new Xls() : new Xlsx(); Defensive patterns
Strategy: validation
Validate before calling
function looksLikeOle(string $file): bool
{
$fh = fopen($file, 'rb');
$sig = $fh ? (string) fread($fh, 8) : '';
return $sig === "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1";
}
if (!looksLikeOle($file)) { /* route to Xlsx/Csv reader or reject */ } Try / catch
try { $spreadsheet = $reader->load($file); }
catch (\PhpOffice\PhpSpreadsheet\Reader\Exception $e) {
if (str_contains($e->getMessage(), 'OLE container')) {
// wrong format: re-route by content (IOFactory::load) or ask for a re-export
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file);
} else { throw $e; }
} Prevention
- Choose readers by magic bytes, never by file extension.
- Use IOFactory::createReaderForFile()/load() as the default entry point for arbitrary uploads.
- Tell users to export real .xls/.xlsx rather than renaming files, and validate on upload.
When it happens
Trigger: Feeding a non-OLE file into the .xls reading path: passing an .xlsx (starts with 'PK'), a CSV, or an HTML table saved with an .xls extension to code that parses it as an OLE container; or a truncated/corrupted .xls whose header was overwritten. The check is a strict byte comparison of fread($fh, 8).
Common situations: Users renaming report.xlsx to report.xls (or exports mislabeled by mime-type); code hardcoding Reader\Xls / OLE parsing for any .xls-named upload; files corrupted in transfer or truncated by upload limits; CSV data with an .xls extension coming from third-party ERP exports.
Related errors
- The filename $filename is not recognised as an OLE file
- Only Little-Endian encoding is supported.
- Unsupported PPS type
- $bitmap: only 1 plane supported in bitmap image.\n
- Invalid R1C1-format Cell Reference
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/851e2eb98ccd3584.
Report an issue: GitHub.