ramsey/uuid · error · InvalidArgumentException

The byte string must be 16 bytes long; received {bytes} byte

Error message

The byte string must be 16 bytes long; received {bytes} bytes

What it means

Nonstandard\Fields is the lenient fields implementation used by Nonstandard\UuidBuilder (the builder behind Ulid and other non-RFC layouts). It performs only one structural check: the byte string must be exactly 16 bytes (128 bits); variant and version are not validated. Any other length throws InvalidArgumentException.

Source

Thrown at src/Nonstandard/Fields.php:56

 *
 * Internally, this class represents the fields together as a 16-byte binary string.
 *
 * @immutable
 */
final class Fields implements FieldsInterface
{
    use SerializableFieldsTrait;
    use VariantTrait;

    /**
     * @param string $bytes A 16-byte binary string representation of a UUID
     *
     * @throws InvalidArgumentException if the byte string is not exactly 16 bytes
     */
    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',
            );
        }
    }

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

    public function getClockSeq(): Hexadecimal
    {
        $clockSeq = hexdec(bin2hex(substr($this->bytes, 8, 2))) & 0x3fff;

        return new Hexadecimal(str_pad(dechex($clockSeq), 4, '0', STR_PAD_LEFT));
    }

    public function getClockSeqHiAndReserved(): Hexadecimal

View on GitHub (pinned to da5b521600)

Solutions

  1. Verify strlen($bytes) === 16 before building; convert with exactly 32 hex chars
  2. Use Ulid::fromString()/fromBytes() for ULID parsing so earlier validation gives clearer errors
  3. Add a length assertion at your storage boundary (e.g. VARBINARY(16) columns)

Example fix

// before
$ulid = \Ramsey\Uuid\Nonstandard\Ulid::fromBytes($value);

// after
if (strlen($value) !== 16) {
    throw new InvalidArgumentException('ULID bytes must be 16, got ' . strlen($value));
}
$ulid = \Ramsey\Uuid\Nonstandard\Ulid::fromBytes($value);
Defensive patterns

Strategy: validation

Validate before calling

if (strlen($bytes) !== 16) {
    throw new InvalidArgumentException('Expected 16 bytes, got ' . strlen($bytes));
}
$ulid = \Ramsey\Uuid\Nonstandard\Ulid::fromBytes($bytes);

Type guard

function isSixteenByteString(string $bytes): bool
{
    return strlen($bytes) === 16;
}

Try / catch

try {
    $ulid = \Ramsey\Uuid\Nonstandard\Ulid::fromBytes($bytes);
} catch (\Ramsey\Uuid\Exception\InvalidArgumentException $e) {
    // length or upstream parse failure; re-fetch or reject the payload
}

Prevention

When it happens

Trigger: Calling Nonstandard\UuidBuilder::build($codec, $bytes) (used by Ulid::fromString()/fromBytes internally) or constructing Nonstandard\Fields directly with a byte string whose length differs from 16 — e.g. hex2bin('abc') on odd-length hex, a truncated ULID byte payload, or appending a prefix/suffix to the binary form.

Common situations: Custom codecs or builders for ULID/nonstandard identifiers; binary round-trips through columns or queues that truncate; passing hex strings where the API expects raw bytes.

Related errors


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