ramsey/uuid · error · InvalidArgumentException

$bytes string should contain 16 characters.

Error message

$bytes string should contain 16 characters.

What it means

OrderedTimeCodec::decodeBytes() expects the 16-byte rearranged binary form that OrderedTimeCodec::encodeBinary() produces. It throws InvalidArgumentException the moment strlen($bytes) !== 16, before any byte rearranging happens. Anything that is not raw 16-byte ordered-time data - hex text, hyphenated strings, base64, truncated values - fails here.

Source

Thrown at src/Codec/OrderedTimeCodec.php:80

        /** @phpstan-ignore possiblyImpure.methodCall */
        $bytes = $uuid->getFields()->getBytes();

        return $bytes[6] . $bytes[7] . $bytes[4] . $bytes[5]
            . $bytes[0] . $bytes[1] . $bytes[2] . $bytes[3]
            . substr($bytes, 8);
    }

    /**
     * Returns a UuidInterface derived from an ordered-time binary string representation
     *
     * @throws InvalidArgumentException if $bytes is an invalid length
     *
     * @inheritDoc
     */
    public function decodeBytes(string $bytes): UuidInterface
    {
        if (strlen($bytes) !== 16) {
            throw new InvalidArgumentException('$bytes string should contain 16 characters.');
        }

        // Rearrange the bytes to their original order.
        $rearrangedBytes = $bytes[4] . $bytes[5] . $bytes[6] . $bytes[7]
            . $bytes[2] . $bytes[3] . $bytes[0] . $bytes[1]
            . substr($bytes, 8);

        $uuid = parent::decodeBytes($rearrangedBytes);

        /** @phpstan-ignore possiblyImpure.methodCall */
        $fields = $uuid->getFields();

        if (!$fields instanceof Rfc4122FieldsInterface || $fields->getVersion() !== Uuid::UUID_TYPE_TIME) {
            throw new UnsupportedOperationException(
                'Attempting to decode a non-time-based UUID using OrderedTimeCodec',
            );
        }

View on GitHub (pinned to da5b521600)

Solutions

  1. Convert back to raw bytes: (string) hex2bin($hex) or base64_decode($b64) before decoding.
  2. Validate strlen($bytes) === 16 before calling decodeBytes().
  3. Store ordered-time UUIDs in a BINARY(16) column so length cannot drift.

Example fix

// before
$uuid = $orderedCodec->decodeBytes($row['id']); // hex string, 32 chars

// after
$bytes = (string) hex2bin($row['id']);
if (strlen($bytes) !== 16) {
    throw new RuntimeException('expected 16 bytes');
}
$uuid = $orderedCodec->decodeBytes($bytes);
Defensive patterns

Strategy: validation

Validate before calling

function assertOrderedTimeBytes(string $bytes): void
{
    if (strlen($bytes) === 32 && ctype_xdigit($bytes)) {
        throw new InvalidArgumentException('value looks like hex; run hex2bin() first');
    }
    if (strlen($bytes) !== 16) {
        throw new InvalidArgumentException(sprintf('expected 16 bytes, got %d', strlen($bytes)));
    }
}

assertOrderedTimeBytes($bytes);
$uuid = $orderedCodec->decodeBytes($bytes);

Type guard

function isUuidByteString(string $value): bool
{
    return strlen($value) === 16;
}

Try / catch

try {
    $uuid = $orderedCodec->decodeBytes($bytes);
} catch (\Ramsey\Uuid\Exception\InvalidArgumentException $e) {
    // length guard fired: reject or repair the stored value
    throw new StoredIdentifierCorruptException($row['id'], $e);
}

Prevention

When it happens

Trigger: $codec->decodeBytes($hexString) with 32 hex characters; passing the canonical 'xxxxxxxx-xxxx-...' string; decoding a value truncated by transport, column width, or string concatenation.

Common situations: UUID column stored as CHAR(36) or hex text instead of BINARY(16); values hex-encoded for JSON transport and not converted back; reading via an ORM getter that formats bytes as hex.

Related errors


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