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
- 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.
- Convert before assigning: implode arrays, cast or format objects explicitly (e.g. $model->attribute, $dt->format('Y-m-d')).
- Implement __toString() on your value objects that must be writable as cell text.
- 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
- Never assign arrays or bare objects to cells — convert at assignment time (implode, format, JSON-encode if appropriate).
- Give domain objects you export a __toString().
- For writer options that funnel through convertToString, prefer calling it yourself with throw=false and a default when a silent fallback is acceptable.
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
- Cannot check for data validation when cell is not bound to a
- Cannot get data validation for cell that is not bound to a w
- Cannot set data validation for cell that is not bound to a w
- Unsupported binary comparison operator
- Cloning the calculation engine is not allowed!
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/be601a6b6d26cc9a.
Report an issue: GitHub.