uuidv7 improperly accepts dates before 1970-01-01

First seen: 2026-04-25 00:19:44+00:00 · Messages: 5 · Participants: 4

Latest Update

2026-06-01 · claude-opus-4-6

Technical Analysis: uuidv7() Interval Shift Boundary Validation

Core Problem

The uuidv7(interval) function in PostgreSQL accepts arbitrary interval offsets to shift the timestamp embedded in a UUIDv7, but performs no bounds checking on the resulting timestamp value. This creates several problematic scenarios:

  1. Pre-epoch timestamps: uuidv7('-1000 years'::interval) produces a UUID with a negative Unix timestamp, which is invalid per RFC 9562. The spec defines the unix_ts_ms field as an unsigned 48-bit integer representing milliseconds since the Unix epoch (1970-01-01), making negative values semantically meaningless.

  2. Overflow/wraparound: uuidv7('100000 years'::interval) exceeds the 48-bit representable range (~8,919 years from epoch) and silently wraps around, producing a UUID whose embedded timestamp bears no meaningful relationship to the intended time.

  3. Infinity inputs: uuidv7('infinity'::interval) overflows the int64 during epoch conversion, producing garbage output with no diagnostic.

The architectural significance is that UUIDv7 was specifically designed to provide time-ordered identifiers. If the timestamp field can silently wrap around or encode invalid values, the fundamental sortability guarantee is destroyed — and users may not discover this until years of data have been generated with broken ordering.

Design Tension: Flexibility vs. Safety

The central disagreement in this thread is between two design philosophies:

Permissive Approach (Andrey Borodin)

Borodin argues that the overflow behavior was intentionally left unchecked to support a sharding use case: uuidv7(INTERVAL '1000 years' * shard_id) gives each shard its own "time lane" within the 48-bit space, achieving per-shard time-locality without cross-shard ordering. He states the RFC authors confirmed this is compliant. His position is that even pre-epoch offsets produce UUIDs that are still unique, still ascending within a single backend, and thus don't violate documented guarantees.

Strict Approach (Christophe Pettus, Masahiko Sawada, Baji Shaik)

The counter-argument is multi-pronged:

Proposed Solutions

Patch 1: Lower-bound check (Christophe Pettus)

Patch 2: Infinity check (Baji Shaik)

Implied Complete Solution (Sawada's synthesis)

Technical Details of the 48-bit Constraint

The UUIDv7 unix_ts_ms field is exactly 48 bits, representing unsigned milliseconds since Unix epoch:

Any shifted timestamp that falls outside [0, 2^48 - 1] milliseconds produces either a spec-violating UUID or a wrapped-around value that breaks ordering guarantees.

Backward Compatibility Concern

Sawada correctly identifies that adding boundary checks is a behavioral change that could break existing applications already relying on the permissive behavior. However, any such application is already producing spec-noncompliant UUIDs or UUIDs with broken time-ordering, so the "breakage" is arguably surfacing a latent bug rather than removing a feature.

Current Status

The thread has consensus building toward adding boundary validation, with two concrete patches offered and a third (comprehensive) solution being coordinated. No committer has formally objected to the stricter checking. The discussion is at the "agree on direction" stage before a final unified patch is produced.