PHPOffice/PhpSpreadsheet · error · PhpOffice\PhpSpreadsheet\Calculation\Exception
#NUM!
#NUM!
Error message
#NUM!
What it means
#NUM! from the octal validator used by OCT2BIN, OCT2DEC and OCT2HEX. ConvertOctal::validateOctal() counts digits matching [0-7] and throws Calculation\Exception('#NUM!') when the string contains other characters (notably 8 and 9, which are not octal digits) or when it has more than 10 digits - Excel octal numbers are capped at 10 characters with the sign in the top bit.
Source
Thrown at src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php:169
try {
$value = self::validateValue($value);
$value = self::validateOctal($value);
$places = self::validatePlaces($places);
} catch (Exception $e) {
return $e->getMessage();
}
$hexVal = strtoupper(dechex((int) self::toDecimal($value)));
$hexVal = (PHP_INT_SIZE === 4 && strlen($value) === 10 && $value[0] >= '4') ? "FF{$hexVal}" : $hexVal;
return self::nbrConversionFormat($hexVal, $places);
}
protected static function validateOctal(string $value): string
{
$numDigits = (int) preg_match_all('/[01234567]/', $value);
if (strlen($value) > $numDigits || $numDigits > 10) {
throw new Exception(ExcelError::NAN());
}
return $value;
}
}
View on GitHub (pinned to 65b080eef4)
Solutions
- Validate the string against /^[0-7]{1,10}$/ before calling OCT2* functions.
- If the value is really decimal, use ConvertDecimal (DEC2*) functions or decoct() instead of the octal ones.
- Check conversion results for '#NUM!' when the strings come from users or files.
Example fix
// before
$dec = ConvertOctal::toDecimal('789'); // '#NUM!' - 8 and 9 are not octal digits
// after
$oct = trim('789');
$dec = preg_match('/^[0-7]{1,10}$/', $oct)
? ConvertOctal::toDecimal($oct)
: ConvertDecimal::toHex((int) $oct); // it was decimal after all Defensive patterns
Strategy: validation
Validate before calling
$oct = trim((string) $value);
if (!preg_match('/^[0-7]{1,10}$/', $oct)) {
throw new \InvalidArgumentException('value must be 1-10 octal digits (0-7 only)');
}
$dec = ConvertOctal::toDecimal($oct); Type guard
/** OCT2* input: 1-10 characters, digits 0-7 only. */
function isValidOctalString(mixed $v): bool
{
return is_string($v) && preg_match('/^[0-7]{1,10}$/', $v) === 1;
} Prevention
- Watch for decimal-looking strings containing 8 or 9 - they are not octal.
- Validate length: more than 10 digits is rejected.
- If the source is really decimal, use the DEC2* functions instead.
When it happens
Trigger: =OCT2DEC("789") (8/9 invalid), =OCT2DEC("12345678901") (11 digits), =OCT2BIN("12 3") (space); PHP calls ConvertOctal::toDecimal('999') or a decimal phone-number-like string passed by mistake.
Common situations: Passing decimal digit strings that happen to contain 8 or 9 to an octal conversion; digit strings longer than 10 characters (IDs, phone-like codes); values with whitespace or separators from imports.
Related errors
AI-assisted analysis of PHPOffice/PhpSpreadsheet@65b080eef4 (2026-08-17).
Data as JSON: /api/errors/b17df11e1bedc239.
Report an issue: GitHub.