Skip to content
UUID App
Contact
UUID App
Code

How to check if a string is a valid UUID

A tested regular expression for UUIDs, a looser one for messy input, and how to validate in JavaScript and Python without being caught out by Nil, Max, braces or a stray newline.

By The UUID App teamUpdated 6 min read

Quick answer

Match the string against a regular expression that checks the 8-4-4-4-12 hex pattern, a version digit from 1 to 8, and a variant digit of 8, 9, a or b. Add the case-insensitive flag. Then decide whether you also accept the special Nil and Max UUIDs. In JavaScript, the uuid package’s validate() does all of this for you.

The regex

Strict: RFC 9562 versions 1 to 8

/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i

Most of it just checks the shape: 32 hex digits in groups of 8, 4, 4, 4 and 12. Two parts do more:

  • [1-8] is the first digit of the third group. That digit is the version, and RFC 9562 defines versions 1 to 8.
  • [89ab] is the first digit of the fourth group. Its top two bits are the variant, and the standard layout is binary 10, which only allows hex 8, 9, a or b.
  • Random
  • Version, always 4
  • Variant, 8, 9, a or b

The ^ and $ anchors make sure the whole string is a UUID, not just part of it. The i flag accepts uppercase.

Loose: any hex in the right shape

/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i

This one ignores the version and variant. Use it when you accept IDs from other systems that may not follow the standard, such as old Microsoft GUIDs with a c or d variant digit, or test data with a version of 0.

Here is what each one accepts:

Input Strict Loose
9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d Yes Yes
9B1DEB4D-3B7D-4BAD-9BDD-2B0D7B3DCB6D Yes Yes
00000000-0000-0000-0000-000000000000 (Nil) No Yes
9b1deb4d-3b7d-0bad-9bdd-2b0d7b3dcb6d (version 0) No Yes
9b1deb4d-3b7d-4bad-cbdd-2b0d7b3dcb6d (variant c) No Yes
{9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d} No No
9b1deb4d3b7d4bad9bdd2b0d7b3dcb6d No No

Nil and Max

RFC 9562 defines two special values. The Nil UUID is all zeros and the Max UUID is all ones (all f). They are valid UUIDs, but they have no version or variant digits, so the strict regex rejects them.

They are often used as placeholders, for example “no parent” or “end of range”. If your field should allow them, add them as alternatives:

/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i

If the field holds IDs of real records, it is usually better to reject them. See Nil and Max UUID for more.

When was this UUID made? Paste it and see the date. Decode a UUID
Need 10,000 UUIDs? Export to CSV, JSON or SQL. Try it free

In JavaScript

The npm uuid package uses the Nil and Max regex above, so it accepts any case and rejects braces or missing hyphens.

import { validate, version } from 'uuid';

validate('9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d');   // true
validate('{9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d}'); // false
validate('00000000-0000-0000-0000-000000000000');   // true (Nil)

version('9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d');    // 4

// Only accept v7
const isV7 = (s) => validate(s) && version(s) === 7;

version() throws a TypeError for invalid input, so call validate() first. It returns 0 for Nil and 15 for Max.

In Python

The standard library’s uuid.UUID() raises ValueError for bad input, but it is generous. It accepts curly braces, a urn:uuid: prefix, no hyphens at all, and even hyphens in odd places. It also does not check the version or variant. If you want only the standard form, compare against the normalised string:

import uuid

def parse_uuid(s: str) -> uuid.UUID | None:
    try:
        u = uuid.UUID(s)
    except ValueError:
        return None
    if str(u) != s.lower():
        return None  # braces, urn: prefix or missing hyphens
    return u

u = parse_uuid("9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d")
u.version                    # 4
u.variant == uuid.RFC_4122   # True for the standard layout

If you use a regex in Python instead, call fullmatch(). With re.match() and $, a string with a trailing newline still passes, because $ also matches just before a final newline.

import re

UUID_RE = re.compile(
    r"[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}",
    re.IGNORECASE,
)
UUID_RE.fullmatch(s) is not None

Edge cases to decide on

A regex answers “does this look like a UUID?”. These are the choices it leaves to you:

Uppercase

Spaces and newlines

Braces and urn:uuid:

Your database may be looser

Finding UUIDs in longer text

A valid UUID only means the format is right. It does not mean the record exists, or that the person asking is allowed to see it. To check a single ID by hand, paste it into the UUID validator.

Something missing or wrong in this guide?Tell us and we will fix it.

Have a UUID to check right now?

Paste it in and see if it is valid, plus its version and variant.

Open the UUID validator

Keep reading