PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Writer\Exception
Unknown token $token
Error message
Unknown token $token
What it means
After the Xls formula parser builds a token tree, convert() maps each node to a BIFF8 ptg token. The final branch handles only the 'arg' marker and true/false literals; any other token that reaches it has no binary serialization path and triggers 'Unknown token X' with the offending token embedded in the message. This is the parser's catch-all for formula constructs the Xls writer has not implemented.
Source
Thrown at src/PhpSpreadsheet/Writer/Xls/Parser.php:574
}
// commented so argument number can be processed correctly. See toReversePolish().
/*if (Preg::isMatch("/[A-Z0-9\xc0-\xdc\.]+/", $token))
{
return($this->convertFunction($token, $this->_func_args));
}*/
// if it's an argument, ignore the token (the argument remains)
if ($token == 'arg') {
return '';
}
if (Preg::isMatch('/^true$/i', $token)) {
return $this->convertBool(1);
}
if (Preg::isMatch('/^false$/i', $token)) {
return $this->convertBool(0);
}
// TODO: use real error codes
throw new WriterException("Unknown token $token");
}
/**
* Convert a number token to ptgInt or ptgNum.
*
* @param float|int|string $num an integer or double for conversion to its ptg value
*/
private function convertNumber(mixed $num): string
{
// Integer in the range 0..2**16-1
if ((Preg::isMatch('/^\d+$/', (string) $num)) && ($num <= 65535)) {
return pack('Cv', $this->ptg['ptgInt'], $num);
}
// A float
if (BIFFwriter::getByteOrder()) { // if it's Big Endian
$num = strrev((string) $num);
}View on GitHub (pinned to 65b080eef4)
Solutions
- Read the token from the message and locate the cell(s) using it (iterate coordinates and compare getValue() against the token).
- Rewrite the affected formula using constructs the BIFF8 writer supports.
- Save as Xlsx instead - the Xlsx writer serializes the formula string directly and never runs this ptg conversion.
- If you cannot change formulas, write computed values only (setPreCalculateFormulas plus replacing '=...' with cached results) before using the Xls writer.
Example fix
// before
$sheet->getCell('A5')->setValue('=SUMPRODUCT((A1:A3>2)*(B1:B3))');
(new \PhpOffice\PhpSpreadsheet\Writer\Xls($spreadsheet))->save('out.xls');
// WriterException: Unknown token ...
// after: Xlsx writer does not convert formulas to ptg tokens
(new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet))->save('out.xlsx'); Defensive patterns
Strategy: try-catch
Try / catch
try {
(new \PhpOffice\PhpSpreadsheet\Writer\Xls($spreadsheet))->save($path);
} catch (\PhpOffice\PhpSpreadsheet\Writer\Exception $e) {
if (str_starts_with($e->getMessage(), 'Unknown token')) {
// message contains the token; scan cells to find and report the offending formula
$token = trim(substr($e->getMessage(), strlen('Unknown token')));
foreach ($spreadsheet->getAllSheets() as $s) {
foreach ($s->getCoordinates() as $coord) {
$v = $s->getCell($coord)->getValue();
if (is_string($v) && str_contains($v, $token)) {
error_log(sprintf('Xls-incompatible formula in %s!%s: %s', $s->getTitle(), $coord, $v));
}
}
}
}
throw $e;
} Prevention
- Maintain an allow-list of formula functions your Xls export supports and validate incoming formulas against it.
- Test the exact save() path (not just calculation) with representative production formulas before shipping an Xls export feature.
- Consider offering Xlsx as the primary format and Xls only as a validated subset.
When it happens
Trigger: A cell formula containing a token that survives parsing but has no ptg conversion - typically exotic operators, identifiers, or constructs added to the Calculation engine but never taught to the BIFF8 serializer; the message names the exact token that failed.
Common situations: Spreadsheets authored in Excel or imported from Xlsx that use newer syntax, then re-saved as Xls by an automated conversion service; formulas that calculate fine in memory (so tests pass) but explode only at save() time.
Related errors
- Unknown range separator
- ')' token expected.
- Syntax error: $currentToken, lookahead: $lookAhead, current
- Cannot yet write formulae with defined names to Xls
- Unknown sheet name $ext_ref in formula
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/2d35a6a37c38d604.
Report an issue: GitHub.