PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Calculation\Exception
#REF!
#REF!
Error message
#REF!
What it means
Indirect::validateAddress() (Indirect.php:40-48) requires ref_text to be a non-empty string after flattenSingleValue(); anything else - null, numbers, arrays, '' - throws #REF!, which INDIRECT() returns as its result. Excel itself coerces more inputs, so code that 'worked' as a raw Excel formula can fail once the address is assembled in PHP.
Source
Thrown at src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php:44
return Helpers::CELLADDRESS_USE_A1;
}
if (is_string($a1fmt)) {
throw new Exception(ExcelError::VALUE());
}
return (bool) $a1fmt;
}
/**
* Convert cellAddress to string, verify not null string.
*
* @param null|mixed[]|string $cellAddress
*/
private static function validateAddress(array|string|null $cellAddress): string
{
$cellAddress = Functions::flattenSingleValue($cellAddress);
if (!is_string($cellAddress) || !$cellAddress) {
throw new Exception(ExcelError::REF());
}
return $cellAddress;
}
/**
* INDIRECT.
*
* Returns the reference specified by a text string.
* References are immediately evaluated to display their contents.
*
* Excel Function:
* =INDIRECT(cellAddress, bool) where the bool argument is optional
*
* @param mixed[]|string $cellAddress $cellAddress The cell address of the current cell (containing this formula)
* @param mixed $a1fmt Expect bool Helpers::CELLADDRESS_USE_A1 or CELLADDRESS_USE_R1C1,
* but can be provided as numeric which is cast to bool
* @param Cell $cell The current cell (containing this formula)View on GitHub (pinned to 65b080eef4)
Solutions
- Guard that the assembled address is a non-empty string before evaluation
- Substitute defaults for blank components: $addr = $sheetPart . '!' . ($cellPart ?: 'A1')
- Pre-validate the address with Coordinate::indexesFromString() or a cellref regexp before calling INDIRECT
- Fail with an application-level message when address parts are missing
Example fix
// before
$address = $prefix . '!' . $suffix; // $suffix empty -> INDIRECT returns '#REF!'
// after
if ($suffix === null || $suffix === '') {
throw new InvalidArgumentException('INDIRECT address part is missing');
}
$address = $prefix . '!' . $suffix; Defensive patterns
Strategy: type-guard
Validate before calling
if (!is_string($address) || $address === '') {
throw new InvalidArgumentException('INDIRECT ref_text must be a non-empty string');
}
$result = Indirect::INDIRECT($address, $a1, $cell); Type guard
function isNonEmptyString(mixed $value): bool
{
return is_string($value) && $value !== '';
} Try / catch
$result = Indirect::INDIRECT($address, $a1, $cell);
if ($result === '#REF!') {
// ref_text was null/numeric/empty - rebuild the address string
} Prevention
- Validate every concatenated component before building a reference
- Treat missing sheet/cell parts as application errors, not formula errors
- Remember PhpSpreadsheet is stricter than Excel here: numbers are not coerced to addresses
When it happens
Trigger: =INDIRECT(A1) where A1 is blank (null); INDIRECT(5); concatenation producing '' ("Sheet2!" & ""); an array ref_text from an array formula.
Common situations: Dynamically built addresses where one component cell is empty; sheet-name concatenation with missing parts; user-typed addresses from an import form.
Related errors
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/6b62f6052c9f219e.
Report an issue: GitHub.