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

  1. Validate the string against /^[0-7]{1,10}$/ before calling OCT2* functions.
  2. If the value is really decimal, use ConvertDecimal (DEC2*) functions or decoct() instead of the octal ones.
  3. 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

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.