About MongoDB UUID Converter
MongoDB does not have a UUID type. It stores UUIDs as BSON Binary values with a subtype byte that records which convention produced the bytes, and that byte is the entire source of the confusion this tool exists to resolve.
Subtype 4 is the correct, standard encoding: the 16 bytes are in RFC 4122 order, exactly as the UUID reads left to right. Subtype 3 is the legacy encoding, and it is not one format but three - the Java, .NET (C#) and Python drivers each historically wrote subtype 3 with a different byte order. The Java driver reversed bytes 0-7 and bytes 8-15 separately, because it serialised the two 64-bit halves as little-endian longs; the C# driver reversed bytes 0-3, 4-5 and 6-7, matching the Microsoft GUID memory layout; the Python driver wrote them in RFC order. The consequence is that the same UUID written by a Java service and read by a C# service comes back as a completely different value, silently, with no error.
That is why a document inserted by one service appears to have a different _id when queried by another, and why a UUID round-trips correctly within one application but breaks the moment a second language touches the same collection. The bytes are fine; the interpretation is not.
Paste Extended JSON ($binary with base64 and subType), a raw hex string, or a canonical UUID, and this tool shows you the standard UUID string alongside every subtype interpretation, so you can see which convention produced your data and what the value means under each reading.
The fix for new work is to standardise on subtype 4 everywhere, configure your driver's UUID representation explicitly rather than relying on its default, and migrate existing subtype 3 data once - with the byte order of the driver that wrote it, not the one reading it.
How to use the MongoDB UUID Converter
- Paste the value in whatever form you have it: Extended JSON like {"$binary":{"base64":"...","subType":"04"}}, a 32-character hex string, or a canonical 36-character UUID.
- Read off the canonical UUID string along with the interpretation under each subtype convention.
- Identify which driver wrote the data - Java, C# and Python subtype 3 differ - and use that column as the true value.
- Copy the subtype 4 form for anything new, and use the legacy interpretation only to migrate existing documents.
Examples
-
Subtype 04 (RFC 4122)
{ "$binary": { "base64": "VQ6EAOKbQdSnFkRmVUQAAA==", "subType": "04" } } -
Plain GUID after export
550e8400-e29b-41d4-a716-446655440000
MongoDB UUID Converter in code
The same operation this tool performs, in the languages you are most likely to need it.
// What subtype is actually stored?
db.users.findOne({}, { _id: 1 })
// { _id: Binary(Buffer.from("...", "hex"), 4) } <- the 4 is the subtype
// Standard subtype 4 from a UUID string
UUID("550e8400-e29b-41d4-a716-446655440000")
// Explicitly build a legacy subtype 3 value
BinData(3, "VQ6EAOKbQdSnFkRmVUQAAA==")
// Find documents still using the legacy subtype
db.users.find({ _id: { $type: "binData" } }).forEach(d => {
if (d._id.sub_type === 3) print(d._id.toString());
});
import { MongoClient, UUID, Binary } from "mongodb";
// Driver v4+ has a real UUID class that writes subtype 4.
const id = new UUID("550e8400-e29b-41d4-a716-446655440000");
await db.collection("users").insertOne({ _id: id, name: "Ada" });
// Reading a legacy subtype 3 value written by a Java service:
const doc = await db.collection("users").findOne({});
if (doc._id instanceof Binary && doc._id.sub_type === 3) {
const b = Buffer.from(doc._id.buffer);
// JAVA_LEGACY reversed bytes 0-7 and 8-15 separately (it wrote the two
// 64-bit halves as little-endian longs). Reversing each half undoes it.
const fixed = Buffer.concat([
Buffer.from(b.subarray(0, 8)).reverse(),
Buffer.from(b.subarray(8, 16)).reverse(),
]);
console.log(new UUID(fixed).toString());
}
import com.mongodb.MongoClientSettings;
import org.bson.UuidRepresentation;
// Set this explicitly. The default changed between driver versions,
// and relying on it is how collections end up with mixed subtypes.
MongoClientSettings settings = MongoClientSettings.builder()
.uuidRepresentation(UuidRepresentation.STANDARD) // subtype 4
.applyConnectionString(new ConnectionString(uri))
.build();
// UuidRepresentation options:
// STANDARD -> subtype 4, RFC 4122 order (use this)
// JAVA_LEGACY -> subtype 3, bytes 0-7 and 8-15 each reversed
// C_SHARP_LEGACY -> subtype 3, first three fields little-endian
// PYTHON_LEGACY -> subtype 3, RFC 4122 order
from bson.binary import UuidRepresentation
from pymongo import MongoClient
import uuid
# Always state the representation - do not rely on the default.
client = MongoClient(uri, uuidRepresentation="standard") # subtype 4
# Reading legacy data written by a Java service:
java_client = MongoClient(uri, uuidRepresentation="javaLegacy")
# Manual conversion between conventions:
u = uuid.UUID("550e8400-e29b-41d4-a716-446655440000")
standard = u.bytes # RFC 4122 order (subtype 4)
java_legacy = u.bytes[7::-1] + u.bytes[:7:-1] # each 8-byte half reversed
csharp_legacy = u.bytes_le # first three fields little-endian
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
// Register once at startup, before any mapping happens.
BsonSerializer.RegisterSerializer(
new GuidSerializer(GuidRepresentation.Standard)); // subtype 4
// The trap: Guid.ToByteArray() is little-endian for the first three
// fields, which is exactly the C# legacy subtype 3 layout.
var g = Guid.Parse("550e8400-e29b-41d4-a716-446655440000");
byte[] littleEndian = g.ToByteArray(); // legacy order
byte[] rfc4122 = g.ToByteArray(bigEndian: true); // .NET 8+, correct order
// Run against a restored backup first, and record which driver
// wrote the original data - the byte order differs per language.
const cursor = db.collection("users").find({ _id: { $type: "binData" } });
for await (const doc of cursor) {
if (doc._id.sub_type !== 3) continue;
const b = Buffer.from(doc._id.buffer);
// JAVA_LEGACY: reverse bytes 0-7 and 8-15 separately.
const rfc = Buffer.concat([
Buffer.from(b.subarray(0, 8)).reverse(),
Buffer.from(b.subarray(8, 16)).reverse(),
]);
// _id is immutable: insert the corrected document, then remove the old.
await db.collection("users").insertOne({ ...doc, _id: new UUID(rfc) });
await db.collection("users").deleteOne({ _id: doc._id });
}
When you need this
- Working out why a UUID inserted by a Java service reads back differently from a Node or C# service.
- Decoding a $binary value from an Extended JSON export or a mongoexport dump into a readable UUID.
- Auditing a collection that contains a mix of subtype 3 and subtype 4 values.
- Building a migration that rewrites legacy subtype 3 identifiers as standard subtype 4.
- Constructing the exact BinData value needed to query an existing document by its legacy _id.
Common problems and what causes them
- The same UUID reads as a different value in another language
- This is the byte-order difference between the legacy subtype 3 conventions. Java reversed bytes 0-7 and 8-15 separately, C# reversed bytes 0-3, 4-5 and 6-7, Python used RFC order. Determine which driver wrote the data, decode with that convention, and re-encode as subtype 4.
- A query by UUID returns nothing even though the document exists
- Your driver is encoding the query value with a different UUID representation than the one stored. Either set uuidRepresentation to match the stored data, or query with an explicit BinData value of the right subtype and byte order.
- Relying on the driver's default UUID representation
- The defaults have changed across major driver versions, and they differ between languages. A driver upgrade can silently change how new UUIDs are written, leaving one collection with two conventions. Always configure it explicitly.
- Trying to update _id in place during a migration
- _id is immutable. You have to insert a new document with the corrected _id and delete the old one, ideally in a transaction, and you must update anything that referenced the old value.
- Assuming subtype 3 means one specific byte order
- Subtype 3 only says 'legacy UUID' - it does not record which language's convention produced it. That information is not in the data, which is why you need to know what wrote it. Subtype 4 exists precisely to remove this ambiguity.
- Base64 vs hex when reading an export
- Extended JSON puts the 16 bytes in base64 inside $binary; mongodump and some tools show hex. 16 bytes is 24 base64 characters (with padding) or 32 hex characters - if your string is a different length, it is not a bare UUID.
FAQ
- What is the difference between MongoDB UUID subtype 3 and subtype 4?
- Subtype 4 is the standard: 16 bytes in RFC 4122 order, identical to how the UUID string reads. Subtype 3 is the legacy binary form, and its byte order depends on which driver wrote it - Java reversed bytes 0-7 and 8-15 separately, C# reversed bytes 0-3, 4-5 and 6-7, Python used RFC order. Subtype 4 was introduced specifically to end that ambiguity, and it is what all new data should use.
- Why does my Java-written UUID look different in mongosh?
- The Java driver's JAVA_LEGACY representation serialised the two 64-bit halves as little-endian longs, which reverses bytes 0-7 and bytes 8-15 separately. mongosh reads the raw bytes in RFC order, so both halves come back byte-reversed. Reverse each 8-byte half to recover the real UUID, and set uuidRepresentation to STANDARD for anything new.
- How do I convert a MongoDB binary UUID to a string?
- Base64-decode the $binary value to 16 bytes, apply the byte order of the subtype and driver that wrote it, then format as 8-4-4-4-12 hex. Paste the value above and this tool does all three steps and shows every interpretation at once.
- Should I migrate my subtype 3 data to subtype 4?
- Yes, if more than one language will ever read the collection - that ambiguity is a standing source of bugs. Migrate once, using the byte order of the driver that originally wrote the data, and test the whole thing against a restored backup because _id changes mean insert-and-delete rather than update.
- Can I store UUIDs as strings in MongoDB instead?
- You can, and it removes the byte-order problem entirely, at the cost of 36 bytes per value instead of 16 plus a larger index. For a collection of any size the binary form with subtype 4 is the better trade; strings are a reasonable choice for small collections where operational simplicity matters more.
- Is my data uploaded anywhere?
- No. The conversion is pure byte manipulation running in your browser, so pasting a real production _id here does not transmit it. The page also works offline once loaded.
- What is subtype 03 vs 04?
- Subtype 04 follows RFC 4122 byte layout. Subtype 03 is the legacy C# driver layout - decoding may differ from standard GUID strings.
- Can I paste only Base64?
- Use the main UUID converter with Prefer format set to Mongo, or paste the full $binary object for automatic detection.