Common UUID mistakes developers make - cover art

UUID and IDs 19 min read

Common UUID mistakes developers make

May 17, 2026 · 19 min read

UUID bugs rarely announce themselves as "UUID problems." They show up as intermittent 404s, duplicate key errors after migrations, and rows that "look identical" in a GUI but fail equality in SQL. Here are the mistakes we see most often - and how to fix them before production teaches you.

1. Comparing strings without normalization

550E8400-E29B-41D4-A716-446655440000 and lowercase variants are the same UUID. Always lowercase (or always uppercase) before compare, or compare binary forms in the database.

2. Ignoring endianness in binary columns

Copying a UUID from a .NET log into a Java service without conversion scrambles the first three fields. When only "sometimes" wrong, check Microsoft GUID byte order vs RFC network order.

3. Using UUID v4 for time-range queries

Random IDs do not sort by creation time. If product asks "show latest orders," add created_at or migrate PK strategy to UUID v7 / ULID.

4. Treating UUID as a session secret

v4 unpredictability is not a substitute for signed tokens. Use proper session JWTs or opaque server-side sessions with rotation and expiry.

5. CHAR(36) everywhere without measuring index size

Wide random keys bloat secondary indexes on foreign keys. Measure table size, consider BINARY(16), denormalize carefully.

6. Logging UUIDs in wrong encoding

Logging Base64 or escaped JSON without marking encoding causes paste errors into converters. Standardize on canonical hyphenated lowercase in application logs.

Bad:  compare(userIdFromClient, userIdFromDb) // case-sensitive language?
Good: normalize both to lowercase canonical
Better: compare binary(16) in SQL

7. Accepting any 36-char hex from clients

Attackers can probe version bits or inject nil UUIDs. Validate version policy at the edge.

8. Mixing UUID versions in one column without documentation

After a v4 → v7 migration, some rows remain v4 forever. Analytics queries that assume time-ordering from PK will lie. Add a id_version column or document the cutover timestamp. Dashboards should filter version = 7 when testing index locality improvements.

Generators misconfigured in one pod (still emitting v4 while others emit v7) create subtle production drift. Centralize ID creation in a library version pinned in all services, and alert when version nibble distribution shifts.

Incident patterns from real migrations

ETL duplicate keys: Two regional databases both used UUID v4, but one region imported legacy rows with uppercase strings while the warehouse normalized to lowercase - joins failed until a single normalization UDF ran.

Support ticket mismatch: Customer pasted a UUID from a PDF invoice where a hyphen was lost at line break. Validation failed; the fix was OCR-resistant formatting in PDFs and a forgiving parser that reports the first invalid character index.

Cache poisoning: Redis keys used raw client strings; mixed case doubled memory for the same logical user. Normalizing at the gateway fixed hit rate overnight.

Debugging playbook

  1. Normalize string (trim, lowercase, remove braces).
  2. Run through validator - note version nibble.
  3. Dump DB bytes as hex; compare to converter output.
  4. If mismatch in first 8 bytes only, fix endianness mapping.
  5. Add regression test with known cross-platform fixture.

FAQ

Why do my UUIDs work locally but fail in staging?
Often different database drivers or ORM settings for UUID vs string column types.
Should I store UUID with or without hyphens?
Pick one convention per system. Canonical with hyphens is easiest for humans.
Why does Elasticsearch treat my UUID as text?
Map it as keyword with a normalizer (lowercase) or use binary if you control ingest pipelines.
Can GraphQL break UUID compare?
Custom scalars should parse and serialize canonical form. Add tests for uppercase client input.

Further reading: How UUIDs work internally · How to validate UUIDs