{"record":{"id":"5fbfcaadfaa883e9","repo":"PHPOffice/PhpSpreadsheet","slug":"could-not-open-file-filename-for-reading","errorCode":null,"errorMessage":"Could not open file {filename} for reading.","messagePattern":"Could not open file (.+?) for reading\\.","errorType":"exception","errorClass":"PhpOffice\\PhpSpreadsheet\\Reader\\Exception","httpStatus":null,"severity":"error","filePath":"src/PhpSpreadsheet/Reader/BaseReader.php","lineNumber":308,"sourceCode":"        } catch (ReaderException $e) {\n            throw $e;\n        }\n    }\n\n    /**\n     * Open file for reading.\n     */\n    protected function openFile(string $filename): void\n    {\n        $fileHandle = false;\n        if ($filename) {\n            File::assertFile($filename);\n\n            // Open file\n            $fileHandle = fopen($filename, 'rb');\n        }\n        if ($fileHandle === false) {\n            throw new ReaderException('Could not open file ' . $filename . ' for reading.');\n        }\n\n        $this->fileHandle = $fileHandle;\n    }\n\n    /**\n     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).\n     *\n     * @return array<int, array{worksheetName: string, lastColumnLetter: string, lastColumnIndex: int, totalRows: int, totalColumns: int, sheetState: string}>\n     */\n    public function listWorksheetInfo(string $filename): array\n    {\n        throw new PhpSpreadsheetException('Reader classes must implement their own listWorksheetInfo() method');\n    }\n\n    /**\n     * Returns names of the worksheets from a file,\n     * possibly without parsing the whole file to a Spreadsheet object.","sourceCodeStart":290,"sourceCodeEnd":326,"githubUrl":"https://github.com/PHPOffice/PhpSpreadsheet/blob/65b080eef4d9fd11a5796135ab145883e5c3d6a6/src/PhpSpreadsheet/Reader/BaseReader.php#L290-L326","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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","Ensure the configured path is a regular file, not a directory, and lies inside open_basedir","For uploads, move_uploaded_file() into your own temp path with correct ownership before parsing"],"exampleFix":"// before\n$spreadsheet = IOFactory::load('/uploads/report.xlsx'); // exists but unreadable -> throws\n\n// after\nif (!is_file($path) || !is_readable($path)) {\n    throw new RuntimeException(\"Input file missing or unreadable: $path\");\n}\n$spreadsheet = IOFactory::load($path);","handlingStrategy":"validation","validationCode":"if (!is_string($path) || !is_file($path) || !is_readable($path)) {\n    throw new RuntimeException(\"Cannot read spreadsheet file: \" . var_export($path, true));\n}\n$spreadsheet = IOFactory::load($path);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check is_file() && is_readable() before every load; catch permission problems with your own message","Copy uploads to a temp file your process owns before parsing, avoiding races with cleanup jobs","Verify open_basedir coverage for the storage path during deployment checks"],"tags":["file-access","permissions","upload","environment"],"backgroundTag":"file-not-readable","analyzedSha":"65b080eef4d9fd11a5796135ab145883e5c3d6a6","analyzedAt":"2026-08-17T05:40:41.646Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}