PHPOffice/PhpSpreadsheet · error · ReaderException

Unable to open php://memory

Error message

Unable to open php://memory

What it means

After re-encoding a non-UTF-8 file to UTF-8, the Csv reader stores the converted bytes in a php://memory stream. fopen('php://memory') failing means the process could not allocate that memory block — in practice, memory_limit is nearly exhausted by a large input file (converted copy roughly the size of the file). Upstream marks it @codeCoverageIgnore because it is essentially unreachable except under memory pressure.

Source

Thrown at src/PhpSpreadsheet/Reader/Csv.php:317

        if ($this->inputEncoding === self::GUESS_ENCODING) {
            $this->inputEncoding = self::guessEncoding($filename, $this->fallbackEncoding);
        }
        $this->openFile($filename);
        if ($this->inputEncoding !== 'UTF-8') {
            $this->convertNonUtf8($filename);
        }
    }

    protected function convertNonUtf8(string $filename): void
    {
        fclose($this->fileHandle);
        $entireFile = file_get_contents($filename);
        if ($entireFile === false) {
            throw new ReaderException("Unable to get contents of $filename"); // @codeCoverageIgnore
        }
        $fileHandle = fopen('php://memory', 'r+b');
        if ($fileHandle === false) {
            throw new ReaderException('Unable to open php://memory'); // @codeCoverageIgnore
        }
        $this->fileHandle = $fileHandle;
        $data = StringHelper::convertEncoding($entireFile, 'UTF-8', $this->inputEncoding);
        fwrite($this->fileHandle, $data);
        $this->skipBOM();
    }

    public function setTestAutoDetect(bool $value): self
    {
        $this->testAutodetect = $value;

        return $this;
    }

    private function setAutoDetect(?string $value, int $version = PHP_VERSION_ID): ?string
    {
        $retVal = null;
        if ($value !== null && $this->testAutodetect && $version < 90000) {

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Raise memory_limit (ini_set or php.ini -d) to comfortably exceed the file size
  2. Pre-convert the file to UTF-8 externally (iconv -f cp1252 -t utf-8) so convertNonUtf8() is never entered
  3. Split oversized files into chunks and load them incrementally

Example fix

// before
ini_set('memory_limit', '128M');
$spreadsheet = (new Csv())->load('huge-latin1.csv'); // memory stream alloc fails

// after
ini_set('memory_limit', '1G');
$spreadsheet = (new Csv())->load('huge-latin1.csv');
// or better: iconv -f cp1252 -t utf-8 huge-latin1.csv > huge-utf8.csv, then load that
Defensive patterns

Strategy: validation

Validate before calling

function bytesFromShorthand(string $v): int { $u = strtolower(substr($v, -1)); $n = (int) $v; return match ($u) { 'g' => $n * 1024 ** 3, 'm' => $n * 1024 ** 2, 'k' => $n * 1024, default => (int) $v }; }
$headroom = bytesFromShorthand(ini_get('memory_limit')) - memory_get_usage(true);
if (filesize($file) * 2 > $headroom) {
    ini_set('memory_limit', ceil(filesize($file) * 3 / 1048576) . 'M');
}

Prevention

When it happens

Trigger: Loading a large non-UTF-8 CSV (tens/hundreds of MB) with a tight memory_limit; memory already consumed by earlier payloads in the same request before the reader runs.

Common situations: Batch imports on shared hosting with memory_limit=128M; containers with hard memory caps; legacy latin1/cp1252 exports from ERP systems.

Related errors


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