ramsey/uuid · error · UnsupportedOperationException
Attempting to decode a non-time-based UUID using OrderedTime
Error message
Attempting to decode a non-time-based UUID using OrderedTimeCodec
What it means
After rearranging bytes back to standard layout and building the UUID, OrderedTimeCodec::decodeBytes() verifies the result is a version 1 (time-based) RFC 4122 UUID and throws UnsupportedOperationException otherwise. So the input was 16 bytes but did not decode to v1: it is either another version (v4 random, v3/v5 name-based) or a standard-layout v1 binary that was never put through OrderedTimeCodec::encodeBinary().
Source
Thrown at src/Codec/OrderedTimeCodec.php:94
*/
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',
);
}
return $uuid;
}
}
View on GitHub (pinned to da5b521600)
Solutions
- Decode with the same codec family that encoded the bytes: StringCodec::decodeBytes() (or the default factory) for standard-layout v1 binaries.
- Migrate legacy rows once: decode with StringCodec, re-encode with OrderedTimeCodec, so a single layout remains in storage.
- If input may be mixed, decode with StringCodec and branch on $uuid->getFields()->getVersion() === 1 instead of assuming layout.
Example fix
// before $uuid = $orderedTimeCodec->decodeBytes($bytes); // bytes were written by the default codec // after $uuid = $stringCodec->decodeBytes($bytes); // standard layout decodes fine // optional one-time migration to ordered layout: // $orderedBytes = $orderedTimeCodec->encodeBinary($uuid);
Defensive patterns
Strategy: try-catch
Validate before calling
// If rows may be standard-layout, decode with StringCodec first and verify version:
$uuid = $stringCodec->decodeBytes($bytes);
$fields = $uuid->getFields();
if (!$fields instanceof \Ramsey\Uuid\Rfc4122\FieldsInterface || $fields->getVersion() !== Uuid::UUID_TYPE_TIME) {
// not a v1 at all - handle before involving OrderedTimeCodec
} Type guard
function decodesToTimeUuid(string $standardBytes, StringCodec $codec): bool
{
$fields = $codec->decodeBytes($standardBytes)->getFields();
return $fields instanceof \Ramsey\Uuid\Rfc4122\FieldsInterface
&& $fields->getVersion() === \Ramsey\Uuid\Uuid::UUID_TYPE_TIME;
} Try / catch
try {
$uuid = $orderedCodec->decodeBytes($bytes);
} catch (\Ramsey\Uuid\Exception\UnsupportedOperationException $e) {
// Legacy standard-layout row: retry with the standard codec.
$uuid = $stringCodec->decodeBytes($bytes);
// optionally schedule migration: $orderedCodec->encodeBinary($uuid) if v1
} Prevention
- Encode and decode with the same codec family; document which codec wrote each column.
- Run a one-time migration when switching to ordered-time storage so only one layout exists.
- Keep a byte-layout marker (or separate table/column) if mixed layouts are unavoidable.
- Add a contract test that round-trips encodeBinary()/decodeBytes() for your stored data.
When it happens
Trigger: Calling OrderedTimeCodec::decodeBytes() on: the bytes of a v4/v3/v5 UUID; a regular (non-reordered) v1 binary produced by the default codec or StringCodec::encodeBinary(); arbitrary 16 bytes whose version nibble is not 1 after rearranging.
Common situations: Encode/decode codec mismatch after adopting ordered-time storage: legacy rows written by StringCodec (or getBytes() on a default factory) decoded by OrderedTimeCodec; mixed old/new rows in one BINARY(16) column; copy of example code that pairs the wrong codec pair.
Related errors
- $bytes string should contain 16 characters.
- Could not find a suitable builder for the provided codec and
- Expected version 1 (time-based) UUID
- $bytes string should contain 16 characters.
- Invalid UUID string: {encodedUuid}
AI-assisted analysis of ramsey/uuid@da5b521600 (2026-08-21).
Data as JSON: /api/errors/c6b3d761abc3dd1d.
Report an issue: GitHub.