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

#NUM!

#NUM!

Error message

#NUM!

What it means

#NUM! from the hexadecimal validator used by HEX2BIN, HEX2DEC and HEX2OCT. ConvertHex::validateHex() runs on the uppercased value and throws Calculation\Exception('#NUM!') when it contains any character outside [0-9A-F], matching Excel's behaviour for invalid hex numbers.

Source

Thrown at src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php:170

        }

        try {
            $value = self::validateValue($value);
            $value = self::validateHex($value);
            $places = self::validatePlaces($places);
        } catch (Exception $e) {
            return $e->getMessage();
        }

        $decimal = self::toDecimal($value);

        return ConvertDecimal::toOctal($decimal, $places);
    }

    protected static function validateHex(string $value): string
    {
        if (strlen($value) > preg_match_all('/[0123456789ABCDEF]/', $value)) {
            throw new Exception(ExcelError::NAN());
        }

        return $value;
    }
}

View on GitHub (pinned to 65b080eef4)

Solutions

  1. Strip prefixes and non-hex characters first: $hex = preg_replace('/^0x|^#/i', '', trim($v)).
  2. Validate with preg_match('/^[0-9A-F]+$/i', $v)) before calling HEX2* functions.
  3. Convert oversized hex in PHP with hexdec()/gmp instead of the Excel wrappers.

Example fix

// before
$dec = ConvertHex::toDecimal('0x1F'); // '#NUM!'

// after
$hex = strtoupper(preg_replace('/^(0x|#)/i', '', trim('0x1F'))); // '1F'
$dec = ConvertHex::toDecimal($hex);
Defensive patterns

Strategy: validation

Validate before calling

$hex = strtoupper(preg_replace('/^(0x|#)/i', '', trim((string) $value)));
if (!preg_match('/^[0-9A-F]{1,}$/', $hex)) {
    throw new \InvalidArgumentException('value contains non-hex characters');
}
$dec = ConvertHex::toDecimal($hex);

Type guard

/** HEX2* input: hex digits only after stripping 0x/# prefixes. */
function isValidHexString(mixed $v): bool
{
    return is_string($v) && preg_match('/^[0-9A-Fa-f]+$/', $v) === 1;
}

Prevention

When it happens

Trigger: =HEX2DEC("G1"), =HEX2DEC("0x1F") ('X' is invalid after uppercasing), =HEX2BIN("1F ") (trailing space); PHP calls ConvertHex::toDecimal('XYZ') or values with # color prefixes like '#FF' ('#' fails).

Common situations: Hex strings carrying '0x' or '#' prefixes from programming/design contexts; mixed-case is fine (value is uppercased first) but any separator, sign or whitespace is not; values pasted from colour pickers or GUID fragments.

Related errors


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