ramsey/uuid · error · InvalidArgumentException

The byte string received does not contain a valid version

Error message

The byte string received does not contain a valid version

What it means

Guid\Fields uses VersionTrait::isCorrectVersion(): for non-nil, non-max UUIDs the version nibble (high nibble of byte 6 after the GUID byte swap, i.e. the first character of the string's 3rd group) must be one of 1-8. Versions 0 and 9-f are not defined by RFC 9562, so the constructor rejects them with InvalidArgumentException.

Source

Thrown at src/Guid/Fields.php:77

     * @throws InvalidArgumentException if the byte string does not contain a valid version
     */
    public function __construct(private string $bytes)
    {
        if (strlen($this->bytes) !== 16) {
            throw new InvalidArgumentException(
                'The byte string must be 16 bytes long; received ' . strlen($this->bytes) . ' bytes',
            );
        }

        if (!$this->isCorrectVariant()) {
            throw new InvalidArgumentException(
                'The byte string received does not conform to the RFC 9562 (formerly RFC 4122) '
                . 'or Microsoft Corporation variants',
            );
        }

        if (!$this->isCorrectVersion()) {
            throw new InvalidArgumentException('The byte string received does not contain a valid version');
        }
    }

    public function getBytes(): string
    {
        return $this->bytes;
    }

    public function getTimeLow(): Hexadecimal
    {
        // Swap the bytes from little endian to network byte order.
        /** @var string[] $hex */
        $hex = unpack(
            'H*',
            pack(
                'v*',
                hexdec(bin2hex(substr($this->bytes, 2, 2))),
                hexdec(bin2hex(substr($this->bytes, 0, 2))),

View on GitHub (pinned to da5b521600)

Solutions

  1. Check the version nibble before constructing: hex digit at position 12 of the hex string must be 1-8 (nil/max all-zero or all-f are also allowed)
  2. Only convert RFC-conformant 128-bit values to Guid; keep opaque values as hex/binary
  3. Catch UnableToBuildUuidException at the parse boundary when input is untrusted

Example fix

// before
$guid = \Ramsey\Uuid\Guid\Guid::fromBytes($bytes);

// after
$hex = bin2hex($bytes);
$version = hexdec($hex[12]);
if ($hex !== str_repeat('0', 32) && $hex !== str_repeat('f', 32) && ($version < 1 || $version > 8)) {
    throw new InvalidArgumentException('Not an RFC 9562 version');
}
$guid = \Ramsey\Uuid\Guid\Guid::fromBytes($bytes);
Defensive patterns

Strategy: validation

Validate before calling

$hex = bin2hex($bytes);
$version = hexdec($hex[12]);
$isSpecial = $hex === str_repeat('0', 32) || $hex === str_repeat('f', 32);
if (!$isSpecial && ($version < 1 || $version > 8)) {
    throw new InvalidArgumentException('Invalid UUID version nibble: ' . $hex[12]);
}

Type guard

function hasGuidValidVersion(string $bytes): bool
{
    $hex = bin2hex($bytes);
    if ($hex === str_repeat('0', 32) || $hex === str_repeat('f', 32)) {
        return true;
    }
    $v = hexdec($hex[12]);
    return $v >= 1 && $v <= 8;
}

Try / catch

try {
    $guid = \Ramsey\Uuid\Guid\Guid::fromBytes($bytes);
} catch (\Ramsey\Uuid\Exception\UnableToBuildUuidException $e) {
    // check message for 'valid version' to distinguish from variant/length failures
}

Prevention

When it happens

Trigger: Building a Guid from 16 bytes whose version nibble is 0 or 9-f, e.g. Guid::fromBytes(hex2bin('1234567812349abc8123456789abcdef')) — the '9' after the second group makes version 9 invalid. Also reached via custom builders feeding GuidBuilder::build().

Common situations: Parsing identifiers produced by non-RFC systems (ULIDs converted naively, content-hash derived 128-bit values, V1 UUIDs bit-shifted wrongly); corrupted or truncated binary payloads where the version nibble was damaged.

Related errors


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