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

Syntax error: $currentToken, lookahead: $lookAhead, current

Error message

Syntax error: $currentToken, lookahead: $lookAhead, current char: $currentCharacter

What it means

The catch-all of the Xls formula parser's fact() step: after failing to match the current token as a parenthesis group, cell reference, number, string, function, boolean, or defined-name-shaped identifier, it throws 'Syntax error: {token}, lookahead: {lookAhead}, current char: {char}' exposing the exact parse position. It means the formula contains syntax the BIFF8 writer's grammar does not accept at all.

Source

Thrown at src/PhpSpreadsheet/Writer/Xls/Parser.php:1514

                . Calculation::CALCULATION_REGEXP_DEFINEDNAME
                . '$/miu',
                $this->currentToken
            )
            && $this->spreadsheet->getDefinedName($this->currentToken) !== null
        ) {
            $result = $this->createTree('ptgName', $this->currentToken, '');
            $this->advance();

            return $result;
        }
        if (Preg::isMatch('/^true|false$/i', $this->currentToken)) {
            $result = $this->createTree($this->currentToken, '', '');
            $this->advance();

            return $result;
        }

        throw new WriterException('Syntax error: ' . $this->currentToken . ', lookahead: ' . $this->lookAhead . ', current char: ' . $this->currentCharacter);
    }

    /**
     * It parses a function call. It assumes the following rule:
     * Func -> ( Expr [,Expr]* ).
     *
     * @return mixed[] The parsed ptg'd tree on success
     */
    private function func(): array
    {
        $num_args = 0; // number of arguments received
        $function = strtoupper($this->currentToken);
        $result = ''; // initialize result
        $this->advance();
        $this->advance(); // eat the "("
        while ($this->currentToken !== ')') {
            if ($num_args > 0) {
                if ($this->currentToken === ',' || $this->currentToken === ';') {

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Use the diagnostic fields in the message: the token plus lookahead identifies the offending construct; search your cells for it.
  2. Rewrite unsupported constructs (arrays -> cell ranges, table refs -> plain ranges).
  3. Normalize localized separators and strip stray characters when accepting user formulas.
  4. Save as Xlsx - its writer emits the formula string without this parse, so syntax the BIFF8 grammar rejects still round-trips.

Example fix

// before
$sheet->getCell('C3')->setValue('=SUM(Table1[Amount])'); // structured reference
(new \PhpOffice\PhpSpreadsheet\Writer\Xls($spreadsheet))->save('out.xls');
// Syntax error: Table1[Amount], lookahead: ...

// after: plain range equivalent
$sheet->getCell('C3')->setValue('=SUM(Sheet2!A2:A99)');
// or keep the original formula and write Xlsx instead
(new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet))->save('out.xlsx');
Defensive patterns

Strategy: try-catch

Validate before calling

/** Reject formula constructs the BIFF8 grammar cannot parse before saving Xls. */
function formulaIsBiff8Safe(string $v): bool
{
    if ($v === '' || $v[0] !== '=') {
        return true;
    }
    // structured references: Table1[Col], array constants: {1,2}, sheet-qualified refs handled elsewhere
    if (preg_match('/\w+\[[^]]*\]/', $v) || preg_match('/\{[\d.," ]+\}/', $v)) {
        return false;
    }

    return true;
}

foreach ($sheet->getCoordinates() as $coord) {
    $v = $sheet->getCell($coord)->getValue();
    if (is_string($v) && !formulaIsBiff8Safe($v)) {
        throw new RuntimeException("Formula not supported by Xls writer at {$coord}: {$v}");
    }
}

Try / catch

try {
    (new \PhpOffice\PhpSpreadsheet\Writer\Xls($spreadsheet))->save($path);
} catch (\PhpOffice\PhpSpreadsheet\Writer\Exception $e) {
    if (str_starts_with($e->getMessage(), 'Syntax error:')) {
        // message contains token + lookahead: find the cell, then fall back to Xlsx or report
        $token = substr($e->getMessage(), strlen('Syntax error:'));
        // ... locate and log offending cells, or:
        (new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet))->save(preg_replace('/\.xls$/', '.xlsx', $path));
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Formulas using constructs outside the parser grammar: array constants ({1,2;3,4}), structured/table references (Table1[Amount]), the intersection (space) or union (,) operators, localized argument separators typed as text, or stray characters from string concatenation bugs.

Common situations: Copy-pasting modern Excel formulas into a system that exports legacy Xls; users pasting from localized Excel where list separators were ',' vs ';'; formula fragments joined with a missing operator ('=A1B2'); data imports that leave stray quotes/whitespace inside formulas.

Related errors


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