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

Line ending must be \n (Unix) or \r\n (Windows)

Error message

Line ending must be \n (Unix) or \r\n (Windows)

What it means

The HTML writer serializes output using a line-ending setting restricted to exactly two sequences: Unix LF and Windows CRLF. setLineEnding() enforces that whitelist and throws for anything else, including a lone CR, an empty string, or markup like '<br>'. The default is PHP_EOL, which is always one of the accepted values on every platform.

Source

Thrown at src/PhpSpreadsheet/Writer/Html.php:92

     */
    protected string $imagesRoot = '';

    /**
     * embed images, or link to images.
     */
    protected bool $embedImages = false;

    protected string $lineEnding = PHP_EOL;

    public function getLineEnding(): string
    {
        return $this->lineEnding;
    }

    public function setLineEnding(string $lineEnding): self
    {
        if ($lineEnding != "\n" && $lineEnding !== "\r\n") {
            throw new Exception('Line ending must be \n (Unix) or \r\n (Windows)');
        }
        $this->lineEnding = $lineEnding;

        return $this;
    }

    protected bool $dataFormula = false;

    public function setDataFormula(bool $dataFormula): self
    {
        $this->dataFormula = $dataFormula;

        return $this;
    }

    protected bool $preserveFormatAndValue = false;

    public function setPreserveFormatAndValue(bool $preserveFormatAndValue): self

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Pass one of the two allowed literals: LF or CRLF.
  2. Omit the call entirely; the default PHP_EOL is always accepted.
  3. Whitelist configuration before applying it: only accept the two exact sequences, otherwise fall back to PHP_EOL or reject the config.

Example fix

// before
$writer->setLineEnding('<br>'); // throws: only LF or CRLF allowed

// after
$writer->setLineEnding(chr(10));          // LF, Unix
$writer->setLineEnding(chr(13) . chr(10)); // CRLF, Windows
// or omit the call: the default PHP_EOL is always accepted
Defensive patterns

Strategy: validation

Validate before calling

$eol = $config['line_ending'] ?? PHP_EOL;
$allowed = [chr(10), chr(13) . chr(10)]; // LF, CRLF
if (!in_array($eol, $allowed, true)) {
    $eol = PHP_EOL; // or reject the configuration value
}
$writer->setLineEnding($eol);

Prevention

When it happens

Trigger: setLineEnding() with a lone carriage return, an empty string, '<br>', or PHP_EOL concatenated with extra characters; passing a user-configurable newline setting straight from an export configuration screen; reusing an EOL constant list meant for CSV, which also permits a third value.

Common situations: Export settings UIs exposing free-text newline fields; shared newline constants between CSV and HTML writers where the CSV-legal lone CR leaks into the HTML writer.

Related errors


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