PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Exception

Unable to convert to string

Error message

Unable to convert to string

What it means

Thrown by StringHelper::convertToString() when the value cannot be losslessly stringified: it is neither null, a scalar (int/float/bool/string), nor an object implementing Stringable — and the method was called with $throw = true (the default). With $throw = false it would instead return the $default string. Most writers and cell-string paths funnel through this helper, so the exception surfaces at save time.

Source

Thrown at src/PhpSpreadsheet/Shared/StringHelper.php:765

            if (!Preg::isMatch('/[^-+0-9.]/', $string)) {
                $minus = $value < 0 ? '-' : '';
                $positive = abs($value);
                $floor = floor($positive);
                $oldFrac = (string) ($positive - $floor);
                $frac = Preg::replace('/^0[.](\d+)$/', '$1', $oldFrac);
                if ($frac !== $oldFrac) {
                    return "$minus$floor.$frac";
                }
            }

            return $string;
        }
        if ($value === null || is_scalar($value) || $value instanceof Stringable) {
            return (string) $value;
        }

        if ($throw) {
            throw new SpreadsheetException('Unable to convert to string');
        }

        return $default;
    }

    /**
     * Assist with POST items when samples are run in browser.
     * Never run as part of unit tests, which are command line.
     *
     * @codeCoverageIgnore
     */
    public static function convertPostToString(string $index, string $default = ''): string
    {
        if (isset($_POST[$index])) {
            return htmlentities(self::convertToString($_POST[$index], false, $default));
        }

        return $default;

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Find the offending cell/option: the exception lacks the coordinate, so log the value type (get_debug_type($value)) in a wrapping try/catch around save() and bisect which cell holds it.
  2. Convert before assigning: implode arrays, cast or format objects explicitly (e.g. $model->attribute, $dt->format('Y-m-d')).
  3. Implement __toString() on your value objects that must be writable as cell text.
  4. If a fallback is acceptable at that call site, use StringHelper::convertToString($value, false, '') style semantics (or pre-normalize) so non-stringifiable values become '' instead of throwing.

Example fix

// before
$sheet->setCellValue('A1', $row); // $row is an array from PDO
(new \PhpOffice\PhpSpreadsheet\Writer\Csv($sheet))->save('out.csv');
// Unable to convert to string

// after
$sheet->setCellValue('A1', is_array($row) ? implode(';', $row) : (string) $row);
// or give value objects a __toString() and cast explicitly
Defensive patterns

Strategy: type-guard

Validate before calling

function toCellString(mixed $value): string
{
    return match (true) {
        $value === null => '',
        is_scalar($value) => (string) $value,
        $value instanceof Stringable => (string) $value,
        default => '', // or throw your own descriptive error
    };
}

Type guard

function isStringifiable(mixed $value): bool
{
    return $value === null || is_scalar($value) || $value instanceof Stringable;
}

if (!isStringifiable($cellValue)) {
    throw new InvalidArgumentException('Cell value must be scalar or Stringable, got ' . get_debug_type($cellValue));
}

Try / catch

try { $writer->save($path); }
catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
    if ($e->getMessage() === 'Unable to convert to string') {
        // value is array/plain object: log the type, fix the data source, re-save
        throw new RuntimeException('Non-stringifiable cell value encountered during export.', 0, $e);
    }
    throw $e;
}

Prevention

When it happens

Trigger: Setting a cell value (or writer option) to a PHP array, a plain stdClass, a SimpleXMLElement/DOMNode, an enum without __toString, or a Closure — then calling a writer's save() or any API that calls StringHelper::convertToString($value) with default flags. Example: $sheet->setCellValue('A1', ['a' => 1]) followed by Csv/Xls export.

Common situations: Writing DB rows (arrays from PDOfetchAll) directly into cells; passing ORM entity objects without __toString; passing enums/DOM nodes from XML processing; inconsistent types arriving from JSON payloads.

Related errors


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