PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Exception
Unserialize not permitted
Error message
Unserialize not permitted
What it means
PhpSpreadsheet's Shared\XMLWriter can buffer its output into a temporary file whose handle cannot survive PHP serialization. To prevent a resurrected writer with a dangling/missing temp file, __unserialize() deliberately throws. It is a hard guard: there is no supported way to unserialize an XMLWriter instance (the method is planned to disappear once PHP 8.6 makes unserialization of internal classes impossible).
Source
Thrown at src/PhpSpreadsheet/Shared/XMLWriter.php:80
if ($this->tempFileName != '') {
@unlink($this->tempFileName);
}
}
/**
* Unserialization is not allowed. This needs to be enforced
* in the class before Php8.6, but, with that release,
* this method is no longer needed and will not be executed.
*
* @see https://github.com/php/php-src/pull/21694
*
* @param mixed[] $data
*/
public function __unserialize(array $data): void
{
$this->tempFileName = '';
throw new SpreadsheetException('Unserialize not permitted');
}
/**
* Get written data.
*/
public function getData(): string
{
if ($this->tempFileName == '') {
return $this->outputMemory(true);
}
$this->flush();
return file_get_contents($this->tempFileName) ?: '';
}
/**
* Wrapper method for writeRaw.
*View on GitHub (pinned to 65b080eef4)
Solutions
- Do not serialize/unserialize PhpSpreadsheet objects; store the finished artifact instead (the written XLSX/CSV file path, or the XML string from XMLWriter::getData()).
- Remove Spreadsheet/Writer/XMLWriter instances from the data you pass to serialize()/cache/queue payloads before they get persisted.
- If you only need the writer's output, cache getData() (the XML string) and rebuild a fresh XMLWriter when needed.
- If a third-party serializer hits it, wrap the payload in a serializable DTO that excludes the writer (e.g. implement __sleep/__serialize on your wrapper to strip it).
Example fix
// before
$blob = unserialize($redis->get('writer'));
// throws: Unserialize not permitted
// after
$xml = $redis->get('writer-output'); // cache the string from ->getData()
$writer = new \PhpOffice\PhpSpreadsheet\Shared\XMLWriter();
$writer->writeRaw($xml); Defensive patterns
Strategy: validation
Validate before calling
// Never feed PhpSpreadsheet objects to unserialize(); inspect payloads before unserializing
function payloadIsSafeToUnserialize(string $blob): bool
{
return !str_contains($blob, 'PhpOffice\\PhpSpreadsheet\\Shared\\XMLWriter');
} Type guard
function isSafeForSerialization(mixed $value): bool
{
return !($value instanceof \PhpOffice\PhpSpreadsheet\Shared\XMLWriter)
&& !($value instanceof \PhpOffice\PhpSpreadsheet\Spreadsheet);
} Try / catch
try {
$obj = unserialize($blob);
} catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
// rebuild from source data instead of the cached object
$obj = buildFreshFromSource();
} Prevention
- Cache serialized output (strings/paths), never PhpSpreadsheet objects.
- Keep Spreadsheet/Writer instances out of queue payloads and sessions.
- Audit deep-clone helpers that use serialize()/unserialize().
When it happens
Trigger: Calling unserialize() on a byte string that contains a serialized PhpOffice\PhpSpreadsheet\Shared\XMLWriter (directly or nested inside another object); caching serialized PhpSpreadsheet objects (Worksheet/Writer/Reader internals hold XMLWriter instances) in Redis, sessions, queues, or message payloads; generic deep-clone helpers implemented via serialize()/unserialize().
Common situations: Queues (Laravel, Symfony Messenger) that serialize job payloads carrying a Spreadsheet or a Writer; session or cache drivers that serialize whole objects; upgrading from a workflow that stored half-built export objects. The error surfaces at unserialize time, far from where the object was stored.
Related errors
- Unsupported binary comparison operator
- Cloning the calculation engine is not allowed!
- Unsupported numeric binary operation
- Locale file not found
- #VALUE!
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/e2ae35d63d72a4b7.
Report an issue: GitHub.