ramsey/uuid · error · InvalidBytesException
Invalid number of bytes
Error message
Invalid number of bytes
What it means
VariantTrait::getVariant() reads the variant bits from the UUID's internal 16-byte string and first asserts strlen($this->getBytes()) === 16, throwing Ramsey\Uuid\Exception\InvalidBytesException ('Invalid number of bytes') when the length differs. The trait is shared by Ramsey\Uuid\Rfc4122\Fields, Guid\Fields and Nonstandard\Fields, whose constructors validate length eagerly — so reaching this throw usually means a custom Fields implementation using VariantTrait, or a byte string that was truncated/corrupted. Any byte count other than exactly 16 (15, 17, 32...) triggers it.
Source
Thrown at src/Rfc4122/VariantTrait.php:58
/**
* Returns the variant
*
* The variant number describes the layout of the UUID. The variant number has the following meaning:
*
* - 0 - Reserved for NCS backward compatibility
* - 2 - The RFC 9562 (formerly RFC 4122) variant
* - 6 - Reserved, Microsoft Corporation backward compatibility
* - 7 - Reserved for future definition
*
* For RFC 9562 (formerly RFC 4122) variant UUIDs, this value should always be the integer `2`.
*
* @link https://www.rfc-editor.org/rfc/rfc9562#section-4.1 RFC 9562, 4.1. Variant Field
*/
public function getVariant(): int
{
if (strlen($this->getBytes()) !== 16) {
throw new InvalidBytesException('Invalid number of bytes');
}
// According to RFC 9562, sections {@link https://www.rfc-editor.org/rfc/rfc9562#section-4.1 4.1} and
// {@link https://www.rfc-editor.org/rfc/rfc9562#section-5.10 5.10}, the Max UUID falls within the range
// of the future variant.
if ($this->isMax()) {
return Uuid::RESERVED_FUTURE;
}
// According to RFC 9562, sections {@link https://www.rfc-editor.org/rfc/rfc9562#section-4.1 4.1} and
// {@link https://www.rfc-editor.org/rfc/rfc9562#section-5.9 5.9}, the Nil UUID falls within the range
// of the Apollo NCS variant.
if ($this->isNil()) {
return Uuid::RESERVED_NCS;
}
/** @var int[] $parts */
$parts = unpack('n*', $this->getBytes());View on GitHub (pinned to da5b521600)
Solutions
- Ensure the byte string is exactly 16 bytes before constructing fields: pad hex to 32 characters with str_pad($hex, 32, '0', STR_PAD_LEFT) before hex2bin()
- Prefer Uuid::fromBytes($bytes) or Uuid::fromString($string) — their validators reject wrong lengths with a clearer error up front
- In a custom Fields implementation, validate strlen($bytes) === 16 in the constructor so the error surfaces at creation time
- Check the storage schema (BINARY(16)) and any transport encoding that might truncate the value
Example fix
// before: hex string is 30 chars -> 15 bytes
$bytes = hex2bin('e4b8adce621111e3b9a19b01aaa2');
$fields = new CustomFields($bytes);
$variant = $fields->getVariant(); // InvalidBytesException: Invalid number of bytes
// after: pad to 32 hex chars so the byte string is exactly 16 bytes
$bytes = hex2bin(str_pad('e4b8adce621111e3b9a19b01aaa2', 32, '0', STR_PAD_LEFT));
$fields = new CustomFields($bytes);
$variant = $fields->getVariant(); // 2 (RFC 9562 variant) Defensive patterns
Strategy: validation
Validate before calling
// Run before building fields from raw bytes
if (strlen($bytes) !== 16) {
throw new \InvalidArgumentException(
sprintf('UUID bytes must be exactly 16 bytes, got %d', strlen($bytes))
);
}
// Or when starting from hex, pad/validate first
$hex = str_pad($hex, 32, '0', STR_PAD_LEFT);
if (strlen($hex) !== 32 || !ctype_xdigit($hex)) {
throw new \InvalidArgumentException('Expected a 32-character hex string');
}
$bytes = hex2bin($hex); Type guard
function isUuidByteString(string $bytes): bool
{
return strlen($bytes) === 16;
} Try / catch
use Ramsey\Uuid\Exception\InvalidBytesException;
try {
$variant = $fields->getVariant();
} catch (InvalidBytesException $e) {
// reject/re-request the input; do not guess a variant
} Prevention
- Always feed exactly 16 bytes (32 hex characters) into field construction
- Prefer Uuid::fromBytes()/fromString() — their validators reject wrong lengths early with clearer errors
- In custom Fields classes, validate length in the constructor
- Use BINARY(16) columns and length-checked transports for UUID storage
When it happens
Trigger: Calling getVariant() on fields built from a byte string that is not exactly 16 bytes — e.g. hex2bin() of a 30-character hex string (15 bytes), substr($bytes, 0, 15) truncation in a custom codec, or a custom Fields class using VariantTrait without validating its input length.
Common situations: Binary UUID columns truncated by a database (CHAR(15) instead of BINARY(16)); custom builders on GUID systems reordering 16 bytes with substr offsets; hand-built hex pads shorter than 32 characters; bitwise copy/paste errors in byte manipulation code.
Related errors
- Fields used to create a UuidV2 must represent a version 2 (D
- Fields used to create a UuidV3 must represent a version 3 (n
- Fields used to create a UuidV4 must represent a version 4 (r
- Fields used to create a UuidV5 must represent a version 5 (n
- Fields used to create a UuidV7 must represent a version 7 (U
AI-assisted analysis of ramsey/uuid@da5b521600 (2026-08-21).
Data as JSON: /api/errors/72cc3e203948606c.
Report an issue: GitHub.