PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Reader\Exception

Can't open file $filename

Error message

Can't open file $filename

What it means

Thrown by Shared\OLE::read() when fopen() on the given path fails, before any OLE parsing starts. It means PHP could not open the file in binary read mode at all — the OLE (.xls container) layer never saw the content. Suppressed error handling (@fopen) makes the exception the only signal of the underlying I/O problem.

Source

Thrown at src/PhpSpreadsheet/Shared/OLE.php:115

    public int $smallBlockSize;

    /**
     * Threshold for big blocks.
     */
    public int $bigBlockThreshold;

    /**
     * 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);

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Verify the file exists and is readable before parsing: is_file($filename) && is_readable($filename), and log the absolute path via realpath() at the failure site.
  2. Fix permissions/ownership so the web-server or worker user can read the file (chmod 644 / correct group).
  3. If the path is user-supplied, resolve it once with realpath() at request start and keep that absolute path for the read.
  4. For uploads, move the file to your own storage (move_uploaded_file) and read from there, never from the raw tmp path twice.

Example fix

// before
$ole->read($path); // Can't open file /tmp/tmpXyz/report.xls

// after
if (!is_string($path) || !is_file($path) || !is_readable($path)) {
    throw new InvalidArgumentException("Excel file missing or unreadable: $path");
}
$ole->read($path);
Defensive patterns

Strategy: validation

Validate before calling

if (!is_string($file) || !is_file($file) || !is_readable($file)) {
    throw new InvalidArgumentException("Excel file missing or unreadable: " . var_export($file, true));
}
$ole->read($file);

Type guard

function readableExcelFile(string $file): ?string
{
    return (is_file($file) && is_readable($file)) ? realpath($file) : null;
}

Try / catch

try { $ole->read($file); }
catch (\PhpOffice\PhpSpreadsheet\Reader\Exception $e) {
    if (str_starts_with($e->getMessage(), "Can't open file")) {
        // missing/unreadable — fix the path or permissions, do not retry blindly
        throw new RuntimeException("Cannot read workbook at $file", 0, $e);
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling $ole->read($filename) (directly or via a reader that uses the PEAR-style OLE class) with a path that does not exist, is not readable by the PHP user, is a directory, or uses a stream wrapper that is disallowed/unregistered. File::assertFile() style checks happen in some callers but read() itself only does the fopen.

Common situations: Reading an uploaded file after it was moved/deleted (tmp_name reused across requests); permission mismatch under FPM when the file was created by CLI/cron; path typos or wrong relative CWD in queue workers; safe-mode-style wrapper restrictions on remote URLs.

Related errors


AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17). Data as JSON: /api/errors/dc46d9d172dd7616. Report an issue: GitHub.