ramsey/uuid · error · InvalidArgumentException

Length must be an even number

Error message

Length must be an even number

What it means

CombGenerator::generate() requires an even byte length (InvalidArgumentException otherwise) because it assembles the value from hexadecimal pairs - the timestamp is produced as hex and padded to TIMESTAMP_BYTES * 2 characters before being packed back to bytes.

Source

Thrown at src/Generator/CombGenerator.php:83

        private NumberConverterInterface $numberConverter
    ) {
    }

    /**
     * @throws InvalidArgumentException if $length is not a positive integer greater than or equal to CombGenerator::TIMESTAMP_BYTES
     *
     * @inheritDoc
     */
    public function generate(int $length): string
    {
        if ($length < self::TIMESTAMP_BYTES) {
            throw new InvalidArgumentException(
                'Length must be a positive integer greater than or equal to ' . self::TIMESTAMP_BYTES
            );
        }

        if ($length % 2 !== 0) {
            throw new InvalidArgumentException('Length must be an even number');
        }

        $hash = '';

        /** @phpstan-ignore greater.alwaysTrue (TIMESTAMP_BYTES constant could change in child classes) */
        if (self::TIMESTAMP_BYTES > 0 && $length > self::TIMESTAMP_BYTES) {
            $hash = $this->generator->generate($length - self::TIMESTAMP_BYTES);
        }

        $lsbTime = str_pad(
            $this->numberConverter->toHex($this->timestamp()),
            self::TIMESTAMP_BYTES * 2,
            '0',
            STR_PAD_LEFT,
        );

        return (string) hex2bin(str_pad(bin2hex($hash), $length - self::TIMESTAMP_BYTES, '0') . $lsbTime);
    }

View on GitHub (pinned to da5b521600)

Solutions

  1. Round the length: $length = (int) ceil($length / 2) * 2; and keep it >= 6.
  2. Validate size configuration at boot so odd lengths never reach generate().
  3. Replace COMB usage with Uuid::uuid7() to drop the constraint entirely.

Example fix

// before
$bytes = $combGenerator->generate(15); // odd -> InvalidArgumentException

// after
$length = max(6, (int) ceil(15 / 2) * 2); // 16
$bytes = $combGenerator->generate($length);
Defensive patterns

Strategy: validation

Validate before calling

$length = max(6, (int) ceil($requestedLength / 2) * 2); // even and >= 6
$bytes = $combGenerator->generate($length);

Type guard

function isValidCombLength(int $length): bool
{
    return $length >= 6 && $length % 2 === 0;
}

Prevention

When it happens

Trigger: $combGenerator->generate(15) or any odd length; computing length dynamically (e.g. strlen-derived) and passing an odd result.

Common situations: Configurable ID sizes computed from user input or column sizes; porting token code that assumed odd sizes were fine; combining this check's sibling error (length < 6) during refactors.

Related errors


AI-assisted analysis of ramsey/uuid@da5b521600 (2026-08-21). Data as JSON: /api/errors/873222b5fe89052a. Report an issue: GitHub.