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

Could not open file {filename} for reading.

Error message

Could not open file {filename} for reading.

What it means

BaseReader::openFile() first asserts the file exists, then attempts fopen($filename, 'rb'). The exception fires only when fopen fails after the existence check — i.e. the path exists but cannot be opened for reading by the PHP process.

Source

Thrown at src/PhpSpreadsheet/Reader/BaseReader.php:308

        } catch (ReaderException $e) {
            throw $e;
        }
    }

    /**
     * Open file for reading.
     */
    protected function openFile(string $filename): void
    {
        $fileHandle = false;
        if ($filename) {
            File::assertFile($filename);

            // Open file
            $fileHandle = fopen($filename, 'rb');
        }
        if ($fileHandle === false) {
            throw new ReaderException('Could not open file ' . $filename . ' for reading.');
        }

        $this->fileHandle = $fileHandle;
    }

    /**
     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
     *
     * @return array<int, array{worksheetName: string, lastColumnLetter: string, lastColumnIndex: int, totalRows: int, totalColumns: int, sheetState: string}>
     */
    public function listWorksheetInfo(string $filename): array
    {
        throw new PhpSpreadsheetException('Reader classes must implement their own listWorksheetInfo() method');
    }

    /**
     * Returns names of the worksheets from a file,
     * possibly without parsing the whole file to a Spreadsheet object.

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Verify with is_file() && is_readable() before calling load(), and fix ownership/permissions (chmod/chown) or copy the file to a location your process owns
  2. Ensure the configured path is a regular file, not a directory, and lies inside open_basedir
  3. For uploads, move_uploaded_file() into your own temp path with correct ownership before parsing

Example fix

// before
$spreadsheet = IOFactory::load('/uploads/report.xlsx'); // exists but unreadable -> throws

// after
if (!is_file($path) || !is_readable($path)) {
    throw new RuntimeException("Input file missing or unreadable: $path");
}
$spreadsheet = IOFactory::load($path);
Defensive patterns

Strategy: validation

Validate before calling

if (!is_string($path) || !is_file($path) || !is_readable($path)) {
    throw new RuntimeException("Cannot read spreadsheet file: " . var_export($path, true));
}
$spreadsheet = IOFactory::load($path);

Prevention

When it happens

Trigger: No read permission on the file (chmod 600 owned by another user); the file is deleted or replaced between assertFile() and fopen() (tmp-cleanup race); open_basedir restrictions on the path; the path is actually a directory; a symlink pointing to an unreadable target.

Common situations: Uploaded files stored with root ownership then read by the web server user; cron jobs purging the temp dir while an import runs; deployment configs with restrictive open_basedir; paths built from configuration pointing at a directory.

Related errors


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