Fixing UUID endianness issues in MongoDB - cover art

MongoDB and UUID 16 min read

Fixing UUID endianness in MongoDB: swap the first three fields

July 12, 2026 · 16 min read

Endianness bugs in MongoDB UUID fields rarely announce themselves as “endianness.” They show up as intermittent 404s, foreign keys that look correct in a GUI but fail findOne, or analytics rows that never join. The underlying issue is almost always the same: sixteen bytes were written using legacy GUID field order (subtype 03) while readers assume RFC 4122 order (subtype 04), or vice versa. Fixing it is a structured permutation of the first three UUID fields - eight bytes for time_low, four for time_mid, four for time_hi_and_version - followed by a disciplined migration.

Symptoms that point to endianness, not bad data

If copying a UUID from Compass into a .NET service returns “document not found,” but the string matches what SQL Server shows, suspect byte order. If only documents created before a driver upgrade fail, suspect a codec change rather than application logic. If two hex dumps of the “same” ID differ only in the first 12 bytes, you are not dealing with corruption - you are dealing with two encodings of one logical value.

Teams sometimes discover the issue only after enabling strict binary comparisons in a new microservice. Older services that compared canonical strings masked the problem because both sides stringify through their local codec before comparing.

A practical diagnosis workflow

Pick one known document ID and export it as Extended JSON with subtype visible. Convert the base64 payload to hex in a trusted tool. Compare against the hex you get by encoding the canonical string with an RFC 4122 library. If the last eight bytes match but the first twelve do not, endianness is confirmed. Repeat with a document created after the suspected regression date to see if both encodings coexist in one collection.

// Permute GUID (subtype 3) bytes to RFC (subtype 4) layout
function guidBytesToRfc(buf16) {
  const b = Buffer.from(buf16);
  const out = Buffer.alloc(16);
  b.copy(out, 0, 0, 4);   out[0]=b[3]; out[1]=b[2]; out[2]=b[1]; out[3]=b[0];
  b.copy(out, 4, 4, 6);   out[4]=b[5]; out[5]=b[4];
  b.copy(out, 6, 6, 8);   out[6]=b[7]; out[7]=b[6];
  b.copy(out, 8, 8, 16);
  return out;
}

Golden tests matter more than cleverness: keep vectors from Microsoft documentation, MongoDB driver samples, and your own production snapshot (sanitized) in the same test file. Run them before and after migration batches.

What “swap the first three fields” means

RFC 4122’s canonical string groups map to three multi-byte fields stored big-endian on the wire for subtype 04. Legacy GUID encoding reverses endianness within each of those fields independently. The trailing 8 bytes (clock sequence and node) typically match between encodings, which is why developers focus on the first half of the hex dump when diffing.

Do not flip the entire 16-byte array - that would produce a different identifier entirely. Use established library functions named along the lines of swapGuidBytes or fromLegacyUuid rather than improvising with array reverse.

Migration script pattern for large collections

Process with a bounded cursor, bulk updates in batches (500–5000 depending on oplog pressure), and idempotent filters such as “subtype equals 03” if you tag errant rows. Update both the binary payload and the subtype byte to 04 in the same write so readers never observe an inconsistent pair. Throttle during peak traffic; endianness fixes are offline hygiene, not hot-path code.

If UUID is the primary key, remember that changing bytes changes the _id value unless you rewrite referencing foreign keys in the same transaction or multi-document transaction where supported. Sometimes it is safer to insert new documents with corrected IDs and deprecate old rows than to mutate _id in place.

Verification, monitoring, and rollback

After each batch, sample documents and assert driver round-trip: binary → string → binary equals original corrected bytes. Monitor application error rates on findById endpoints. Keep a collection-level backup or point-in-time restore window until dual-write validation finishes.

Rollback is reversing the permutation and subtype if you have not yet deleted legacy rows. Document the exact script version in the change ticket so on-call engineers do not run an older script that only updates subtype without permuting bytes - a failure mode worse than the original bug.

Coordinate with cache layers and search indexes: Redis keys and Elasticsearch document IDs that embed raw hex from the wrong subtype will still miss after MongoDB is fixed unless those systems are refreshed or keyed by canonical string instead of legacy bytes.

Schedule migrations during maintenance windows when unique index builds on corrected _id values might be required. Even online index builds add load; communicate expected duration to application owners who might otherwise restart pods mid-batch and duplicate work.

FAQ

Can I fix endianness by changing only the subtype byte?
No. Subtype describes byte order. Changing 03 to 04 without permuting bytes produces a different logical ID than your applications expect.
Is it safe to mutate _id UUID values?
Only with a coordinated plan for foreign keys and caches. Many teams insert corrected documents and archive old ones instead.
How long do migrations take?
Depends on collection size and oplog. Batch with sleep, test on staging with identical indexes, and watch replication lag on secondaries.
Will Atlas handle this automatically?
Atlas stores what you write. Codec configuration and migrations remain application responsibilities.

Related: Subtype 03 vs 04 · Why hex looks different · Binary to string · MongoDB UUID converter

Browse all tools