diff --git a/src/common/uuid.cpp b/src/common/uuid.cpp
index 18303a1e32..d7435a6e95 100644
--- a/src/common/uuid.cpp
+++ b/src/common/uuid.cpp
@@ -6,10 +6,64 @@
 
 #include <fmt/format.h>
 
+#include "common/assert.h"
 #include "common/uuid.h"
 
 namespace Common {
 
+namespace {
+
+bool IsHexDigit(char c) {
+    return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+}
+
+u8 HexCharToByte(char c) {
+    if (c >= '0' && c <= '9') {
+        return static_cast<u8>(c - '0');
+    }
+    if (c >= 'a' && c <= 'f') {
+        return static_cast<u8>(c - 'a' + 10);
+    }
+    if (c >= 'A' && c <= 'F') {
+        return static_cast<u8>(c - 'A' + 10);
+    }
+    ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
+    return u8{0};
+}
+
+} // Anonymous namespace
+
+u128 HexStringToU128(std::string_view hex_string) {
+    const size_t length = hex_string.length();
+
+    // Detect "0x" prefix.
+    const bool has_0x_prefix = length > 2 && hex_string[0] == '0' && hex_string[1] == 'x';
+    const size_t offset = has_0x_prefix ? 2 : 0;
+
+    // Check length.
+    if (length > 32 + offset) {
+        ASSERT_MSG(false, "hex_string has more than 32 hexadecimal characters!");
+        return INVALID_UUID;
+    }
+
+    u64 lo = 0;
+    u64 hi = 0;
+    for (size_t i = 0; i < length - offset; ++i) {
+        const char c = hex_string[length - 1 - i];
+        if (!IsHexDigit(c)) {
+            ASSERT_MSG(false, "{} is not a hexadecimal digit!", c);
+            return INVALID_UUID;
+        }
+        if (i < 16) {
+            lo |= u64{HexCharToByte(c)} << (i * 4);
+        }
+        if (i >= 16) {
+            hi |= u64{HexCharToByte(c)} << ((i - 16) * 4);
+        }
+    }
+    return u128{lo, hi};
+}
+
 UUID UUID::Generate() {
     std::random_device device;
     std::mt19937 gen(device());
diff --git a/src/common/uuid.h b/src/common/uuid.h
index 0ffa37e7cb..aeb36939a3 100644
--- a/src/common/uuid.h
+++ b/src/common/uuid.h
@@ -5,6 +5,7 @@
 #pragma once
 
 #include <string>
+#include <string_view>
 
 #include "common/common_types.h"
 
@@ -12,12 +13,30 @@ namespace Common {
 
 constexpr u128 INVALID_UUID{{0, 0}};
 
+/**
+ * Converts a hex string to a 128-bit unsigned integer.
+ *
+ * The hex string can be formatted in lowercase or uppercase, with or without the "0x" prefix.
+ *
+ * This function will assert and return INVALID_UUID under the following conditions:
+ * - If the hex string is more than 32 characters long
+ * - If the hex string contains non-hexadecimal characters
+ *
+ * @param hex_string Hexadecimal string
+ *
+ * @returns A 128-bit unsigned integer if successfully converted, INVALID_UUID otherwise.
+ */
+[[nodiscard]] u128 HexStringToU128(std::string_view hex_string);
+
 struct UUID {
     // UUIDs which are 0 are considered invalid!
     u128 uuid;
     UUID() = default;
     constexpr explicit UUID(const u128& id) : uuid{id} {}
     constexpr explicit UUID(const u64 lo, const u64 hi) : uuid{{lo, hi}} {}
+    explicit UUID(std::string_view hex_string) {
+        uuid = HexStringToU128(hex_string);
+    }
 
     [[nodiscard]] constexpr explicit operator bool() const {
         return uuid != INVALID_UUID;