uuid
semver
>=9.0.0 <15.0.0postconditions16functions12last verified2026-06-23coverage score100%Postconditions — what we check
- v4 · v4-returns-valid-uuidwarningWhencalled with valid or no arguments in a modern Node.js/browser environmentReturnsstring containing valid UUID v4 format (e.g. '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d')Required handlingWhen accepting UUID from user input (e.g., route params, query strings, request bodies), caller SHOULD use validate() to verify format before using in database queries or business logic. v4() itself always succeeds in modern environments — the risk is accepting malformed UUIDs FROM USERS, not generating them.costmediumin prodsilent failureusers seelost datavisibilitysilentSources[1]
- v4 · v4-crypto-unavailableerrorWhenv4() is called in an environment where crypto.getRandomValues() is not available. Affects: Node.js 14 and below (without --experimental-global-fetch), older React Native versions, certain SSR/edge environments, or worker contexts with restricted crypto access. Also triggered when a custom options.rng function returns a buffer with fewer than 16 bytes, or when writing to a buf argument at an offset that would exceed the buffer bounds.Throws
Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported') — when crypto API is unavailable. Error('Random bytes length must be >= 16') — when custom rng returns short buffer. RangeError('UUID byte range <offset>:<offset+15> is out of buffer bounds') — when writing to a buf argument with invalid offset.Required handlingIn modern Node.js (14.17+) and browsers with Web Crypto API, v4() never throws. For edge environments or older runtimes, wrap in try-catch if crypto availability cannot be guaranteed: try { const id = v4(); return id; } catch (error) { if (error.message.includes('crypto.getRandomValues')) { // Fallback: use a less-random ID or reject with clear error throw new Error('UUID generation not supported in this environment'); } throw error; } For the custom options.rng case, always return exactly 16 bytes: v4({ rng: () => new Uint8Array(16).fill(Math.random() * 256) })costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - v7 · v7-crypto-unavailablewarningWhenv7() is called in an environment where crypto.getRandomValues() is not available, or a custom rng returns fewer than 16 bytes, or the buf offset is out of bounds. Same conditions as v4-crypto-unavailable — v7 uses the same rng() function. Additionally: v7() maintains a global monotonic sequence state — if the same millisecond counter is incremented more than 2^30 times, the millisecond counter is artificially incremented (seq overflow), causing clock skew in the generated UUID timestamps.Throws
Error('Random bytes length must be >= 16') — custom rng too short. RangeError('UUID byte range <offset>:<offset+15> is out of buffer bounds') — buf/offset out of bounds. Same crypto unavailability Error as v4() when crypto.getRandomValues() missing.Required handlingIn practice, v7() never throws in modern Node.js or browser environments. The same safeguards apply as for v4(). For high-throughput UUID generation (>2^30 UUIDs in the same millisecond), use options.seq to manage the counter: import { v7 } from 'uuid'; // Normal use — safe for any real-world throughput const id = v7();costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - v7 · v7-user-input-not-validatederrorWhenApplication uses v7() for database primary keys (the primary use case), but when accepting v7 UUIDs from user input (route params, request bodies), the caller does not call validate() before calling parse() or passing to DB. Example: GET /api/records/:id — if :id is not a valid UUID, parse(id) throws TypeError('Invalid UUID') which becomes an unhandled exception.Throws
TypeError('Invalid UUID') — thrown by parse() when called with invalid input. This is NOT thrown by v7() itself, but is the downstream consequence of using v7-generated IDs in route handlers that accept user-provided IDs.Required handlingAlways validate user-provided UUIDs before using them in DB queries: import { validate, parse } from 'uuid'; function getRecordById(req, res) { const { id } = req.params; if (!validate(id)) { return res.status(400).json({ error: 'Invalid ID format' }); } // Safe to use id in DB query const record = await db.find(id); return res.json(record); } Never call parse(req.params.id) directly without validate() first.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[6] - parse · parse-invalid-uuiderrorWhenparse() is called with a string that is not a valid UUID format, or with any non-string value. Any of these trigger the throw: - Malformed UUID strings ('not-a-uuid', '', 'abc123') - UUIDs with wrong length or character set - null, undefined, or non-string values (TypeScript type error + runtime throw) - User-provided ID from route params (/api/records/:id) without prior validation - UUIDs truncated or modified in transit This is the #1 uuid error in production: route handlers that call parse(req.params.id) directly without checking validate(req.params.id) first.Throws
TypeError with message 'Invalid UUID'. Note: exact message string is 'Invalid UUID' — no period, no URL. In TypeScript: parameter is typed as string, so type errors happen at compile time for non-string inputs. Runtime TypeError fires for valid TypeScript strings that are not valid UUIDs.Required handlingAlways validate before parsing user input: import { parse, validate } from 'uuid'; // Route handler pattern function handleRequest(req, res) { const { id } = req.params; if (!validate(id)) { return res.status(400).json({ error: 'Invalid resource ID', detail: `Expected UUID format, got: ${id}` }); } const bytes = parse(id); // Safe — already validated // ... use bytes for binary DB storage } Alternative — wrap in try-catch when validate() is not called first: try { const bytes = parse(id); } catch (error) { if (error instanceof TypeError) { return res.status(400).json({ error: 'Invalid UUID' }); } throw error; }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - validate · validate-not-called-before-parseerrorWhenApplication receives UUID from user input (URL parameters, request body, query strings, external API responses) and passes directly to parse(), version(), or database query without first calling validate(). Since parse() and version() throw TypeError on invalid input, skipping validate() makes every request handler a potential crash site. Also: validate() should be called before constructing SQL WHERE clauses that include raw UUID strings — even parameterized queries benefit from format validation first.Throws
validate() itself never throws. It returns false for any non-UUID input, including null, undefined, empty string, and malformed UUIDs. However, SKIPPING validate() before parse() or version() causes those functions to throw TypeError('Invalid UUID').Required handlingTreat validate() as a required gate for ALL external UUID inputs: import { validate } from 'uuid'; // Middleware pattern for Express routes with UUID params function validateUUIDParam(paramName) { return (req, res, next) => { const value = req.params[paramName]; if (!validate(value)) { return res.status(400).json({ error: `Invalid ${paramName}: expected UUID format` }); } next(); }; } app.get('/api/users/:id', validateUUIDParam('id'), getUserHandler); For simple inline use: if (!validate(id)) throw new Error(`Invalid UUID: ${id}`); const bytes = parse(id); // Now safecostmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[6] - version · version-invalid-uuidwarningWhenversion() is called with a string that is not a valid UUID. Same conditions as parse-invalid-uuid — any malformed UUID, non-string, or user-provided input that hasn't been validated with validate() first. Also: version() returns unexpected values for v8 UUIDs (experimental) or nil/max UUIDs ('00000000-0000-0000-0000-000000000000' returns 0, 'ffffffff-ffff-ffff-ffff-ffffffffffff' returns 15).Throws
TypeError with message 'Invalid UUID'. Same as parse() — same validation check, same error class, same message.Required handlingGuard with validate() before calling version(): import { validate, version } from 'uuid'; function requireV7(id: string): void { if (!validate(id)) { throw new Error(`Invalid UUID: ${id}`); } if (version(id) !== 7) { throw new Error(`Expected v7 UUID, got v${version(id)}: ${id}`); } }costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - v3 · v3-invalid-namespacewarningWhenv3() is called with a namespace argument that is not a valid UUID string or a 16-byte Uint8Array. Common mistakes: - Passing a custom string that is not a UUID as namespace - Passing a namespace with wrong byte length - Passing null/undefined as namespace - Confusing the name (first arg) with namespace (second arg) argument order Note: v3.DNS and v3.URL are pre-defined safe namespaces from the RFC. Use these when possible instead of custom namespaces.Throws
TypeError('Namespace must be array-like (16 iterable integer values, 0-255)') — when namespace is not a 16-byte array. TypeError('Invalid UUID') — when namespace is a string that is not a valid UUID (thrown by the internal parse() call on the namespace string).Required handlingUse the built-in namespace constants for standard use cases: import { v3 } from 'uuid'; // Safe — using built-in DNS namespace const urlUuid = v3('https://example.com', v3.URL); // Safe — using built-in DNS namespace const dnsUuid = v3('example.com', v3.DNS); // Custom namespace — must be a valid UUID const MY_NAMESPACE = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; const customUuid = v3('my-name', MY_NAMESPACE); Never use arbitrary strings as namespace. Always use UUID format for namespaces.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - v3 · v3-buffer-offset-out-of-boundswarningWhenv3() is called with an explicit `buf` Uint8Array argument AND an `offset` argument that is negative or would write past the buffer end. New in uuid@14.0.0 (v3/v5/v6 previously silently wrote past buffer end). Common in serialization paths that pre-allocate a fixed-size buffer for many UUIDs (e.g., binary protocols that pack multiple UUIDs into one buffer). Triggers when: - offset < 0 (negative offset — common typo from offset-- underflow) - offset + 16 > buf.length (insufficient room — common when buffer size calculation is off-by-one or doesn't account for the 16 bytes per UUID) - Writing many UUIDs in a loop where offset increments incorrectly past buf.lengthThrows
RangeError with message 'UUID byte range <offset>:<offset+15> is out of buffer bounds'. NOT a TypeError — this is a RangeError, which inherits from Error but is a distinct subclass. Catching only TypeError will miss this.Required handlingWhen using the explicit buffer API, validate offset and buffer size first: import { v3 } from 'uuid'; // Pre-allocate enough space for N UUIDs const N = 100; const buf = new Uint8Array(N * 16); for (let i = 0; i < N; i++) { const offset = i * 16; // Defensive check (in addition to the runtime throw) if (offset + 16 > buf.length) { throw new Error(`Buffer too small: need ${(i + 1) * 16}, have ${buf.length}`); } v3(`item-${i}`, v3.URL, buf, offset); } For the common case (no explicit buffer), this postcondition does not apply — v3() returns a new string and never throws RangeError.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - v5 · v5-invalid-namespacewarningWhenv5() is called with a namespace that is not a valid UUID string or 16-byte array. Identical conditions to v3-invalid-namespace — both v3 and v5 use the same internal v35() function with the same validation. Also: v5() generates different UUIDs than v3() for the same name/namespace pair — they are NOT interchangeable. Applications that accidentally swap v3 and v5 in different code paths generate different IDs for the same input, breaking ID lookups.Throws
TypeError('Namespace must be array-like (16 iterable integer values, 0-255)') — when namespace byte array is not 16 bytes. TypeError('Invalid UUID') — when namespace string is not a valid UUID.Required handlingUse v5.DNS or v5.URL namespace constants: import { v5 } from 'uuid'; // Preferred (SHA-1 > MD5 per RFC) const stableId = v5('https://example.com/resource/123', v5.URL); Custom namespace must be a valid UUID string: const NAMESPACE = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; const id = v5('entity-name', NAMESPACE); Never mix v3 and v5 for the same namespace+name pairs — they produce different UUIDs.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - v5 · v5-buffer-offset-out-of-boundswarningWhenv5() is called with an explicit `buf` Uint8Array argument AND an `offset` argument that is negative or would write past the buffer end. New in uuid@14.0.0. Identical mechanism to v3-buffer-offset-out-of-bounds since both v3 and v5 route through the same internal v35() function. Common in protocol-level code that pre-allocates buffers for many stable IDs (e.g., generating IDs from a batch of URLs and packing all 16-byte UUIDs into one Uint8Array).Throws
RangeError with message 'UUID byte range <offset>:<offset+15> is out of buffer bounds'. NOT a TypeError — this is a RangeError. Code that catches only TypeError (from the existing v5-invalid-namespace path) will miss this and crash.Required handlingWhen using the explicit buffer API, validate buffer size and offset first: import { v5 } from 'uuid'; const urls = ['https://a.com', 'https://b.com', 'https://c.com']; const buf = new Uint8Array(urls.length * 16); urls.forEach((url, i) => { const offset = i * 16; if (offset + 16 > buf.length) { throw new Error('Buffer too small for batch'); } v5(url, v5.URL, buf, offset); }); For the common case (no explicit buffer), this postcondition does not apply.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - stringify · stringify-invalid-byteswarningWhenstringify() is called with a Uint8Array whose contents do not encode a valid RFC 4122 UUID — i.e., the version nibble (high 4 bits of byte 6) or variant bits (high 2 bits of byte 8) are not set correctly. Triggers on: - Reading bytes from a binary DB column that may be corrupted or written by a non-UUID-aware writer - Receiving bytes over the wire that were not generated by v1/v3/v4/v5/v6/v7 - Constructing a Uint8Array manually with arbitrary values - Passing an offset into a larger buffer where the 16 bytes at that offset are not actually a UUID - Writing tests with mock UUIDs that don't set the version/variant bits Common in: binary protocol parsers, low-level DB drivers reading BINARY(16) columns that may contain non-UUID data (legacy migration), test fixtures that hand-roll bytes.Throws
TypeError with exact message 'Stringified UUID is invalid'. Distinct from parse()'s 'Invalid UUID' message — when triaging logs, the wording discriminates the failure path (stringify vs parse).Required handlingFor the common case of round-tripping a known-valid UUID, this never fires: import { v4, parse, stringify } from 'uuid'; const id = v4(); const bytes = parse(id); const back = stringify(bytes); // safe — round-trip from a valid UUID console.assert(back === id); When reading bytes from an external source (DB column, network, mock data), wrap in try-catch: try { const idStr = stringify(rowBytes); return idStr; } catch (error) { if (error instanceof TypeError && error.message === 'Stringified UUID is invalid') { // Bytes are corrupt or not a UUID — log and either skip or hard-fail logger.error('Invalid UUID bytes in DB column', { rowId: row.id }); return null; } throw error; } For test fixtures: prefer parse(someUuidString) over hand-rolled byte arrays. If you must hand-roll bytes, use the unsafeStringify export (also available in the source but not in the public API).costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - v1 · v1-crypto-unavailablewarningWhenv1() is called in an environment where crypto.getRandomValues() is not available, or a custom options.rng returns fewer than 16 bytes, or a buf argument is provided with an out-of-bounds offset. Same crypto dependency as v4 and v7 — all three route through the shared rng() function in rng.js. Additional v1-specific shape: callers commonly pass `options.msecs` and `options.nsecs` to override the embedded timestamp (test fixtures, time-travel replay) — these are NOT validated, so a negative or non-finite msecs value produces a malformed UUID without throwing, but the resulting string still fails validate(). Pair with the user-input-validation edge case.Throws
Error('crypto.getRandomValues() not supported. ...') — when crypto API is missing. Error('Random bytes length must be >= 16') — when custom rng returns short buffer. RangeError('UUID byte range <offset>:<offset+15> is out of buffer bounds') — when writing to a buf argument with negative offset or offset + 16 > buf.length.Required handlingIn modern Node.js (14.17+) and browsers with Web Crypto, v1() never throws. For edge environments or custom rng, wrap in try-catch: import { v1 } from 'uuid'; try { const id = v1(); return id; } catch (error) { if (error.message.includes('crypto.getRandomValues')) { throw new Error('UUID v1 generation not supported in this environment'); } throw error; } When using the explicit buffer API, validate offset and size first: const buf = new Uint8Array(N * 16); for (let i = 0; i < N; i++) { const offset = i * 16; if (offset + 16 > buf.length) throw new Error('Buffer too small'); v1(undefined, buf, offset); }costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - v6 · v6-crypto-unavailablewarningWhenv6() is called in an environment where crypto.getRandomValues() is not available, OR with a buf argument where offset < 0 or offset + 16 > buf.length, OR with a custom options.rng that returns fewer than 16 bytes. v6 inherits all of v1's crypto/rng/buf failure modes via its internal v1() call, and adds its own buf-offset RangeError check at the v6 wrapper layer (uuid@14.0.0+). Less common in production than v4/v7 — v7 is the modern sortable choice. v6 mostly appears in code that's migrating from v1 to a sortable format while preserving v1-shaped node/clockseq fields.Throws
Error('crypto.getRandomValues() not supported. ...') — crypto API missing. Error('Random bytes length must be >= 16') — custom rng too short. RangeError('UUID byte range <offset>:<offset+15> is out of buffer bounds') — buf/offset out of bounds (thrown at both the v6 wrapper and the inner v1Bytes call — catching once is sufficient).Required handlingWrap in try-catch when the environment is uncertain: import { v6 } from 'uuid'; try { const id = v6(); return id; } catch (error) { if (error instanceof RangeError) { throw new Error('Buffer too small for UUID v6'); } if (error.message.includes('crypto.getRandomValues')) { throw new Error('UUID v6 generation not supported in this environment'); } throw error; } When generating batch UUIDs into a pre-allocated buffer: const buf = new Uint8Array(N * 16); for (let i = 0; i < N; i++) { const offset = i * 16; if (offset + 16 > buf.length) throw new Error('Buffer too small'); v6(undefined, buf, offset); } Prefer v7() over v6() for new code — same sortability guarantee, simpler internal model, broader ecosystem support (RFC 9562, postgres uuid_v7 functions, etc.).costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - v1ToV6 · v1tov6-invalid-uuidwarningWhenv1ToV6() is called with a string argument that is not a valid UUID format. Common in migration code that maps a database column of v1 UUID strings to v6 — if the column contains any corrupt/missing/legacy non-UUID values (NULL stored as the literal string 'NULL', truncated rows, manual SQL inserts with bad data), the conversion throws TypeError on the first bad row. Also: passing a v4 or v7 UUID (valid UUID format but wrong version) is NOT caught — v1ToV6 will happily produce a garbled v6 from non-v1 input because it operates on byte-level field rearrangement, not version-aware logic. For the Uint8Array overload, v1ToV6 itself does NOT throw — it produces a garbled output for non-v1 bytes, which only fails later if stringified via stringify() (which validates the resulting UUID format).Throws
TypeError('Invalid UUID') — when called with a string that is not a valid UUID. Thrown by the internal parse() call. NOT thrown by the Uint8Array overload — that path silently produces garbage for non-v1 input.Required handlingValidate the input UUID format AND version before calling v1ToV6: import { v1ToV6, validate, version } from 'uuid'; function migrateV1ToV6(legacyId: string): string { if (!validate(legacyId)) { throw new Error(`Invalid UUID in migration: ${legacyId}`); } if (version(legacyId) !== 1) { throw new Error(`Expected v1 UUID, got v${version(legacyId)}: ${legacyId}`); } return v1ToV6(legacyId); } For batch migration with mixed-quality data, wrap in try-catch and quarantine failures: for (const row of rows) { try { row.newId = v1ToV6(row.legacyId); } catch (error) { row.migrationError = error.message; } }costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - v6ToV1 · v6tov1-invalid-uuidwarningWhenv6ToV1() is called with a string that is not a valid UUID format. Same mechanism as v1ToV6-invalid-uuid — the internal parse() call throws TypeError on malformed input. Also: passing a non-v6 UUID (v4, v7, etc.) is NOT caught — v6ToV1 produces a garbled v1 from non-v6 input because it operates on byte-level field rearrangement without version-aware logic. For the Uint8Array overload, v6ToV1 itself does NOT throw — garbled output is only detected later if the result is stringified.Throws
TypeError('Invalid UUID') — when called with a string that is not a valid UUID. Thrown by the internal parse() call. Uint8Array overload does not throw at the v6ToV1 layer.Required handlingValidate format AND version before calling v6ToV1: import { v6ToV1, validate, version } from 'uuid'; function downgradeV6ToV1(modernId: string): string { if (!validate(modernId)) { throw new Error(`Invalid UUID: ${modernId}`); } if (version(modernId) !== 6) { throw new Error(`Expected v6 UUID, got v${version(modernId)}: ${modernId}`); } return v6ToV1(modernId); } For round-trip integrity check after v1ToV6 migration: const original = v1(); const v6 = v1ToV6(original); const back = v6ToV1(v6); console.assert(back === original);costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
Sources
Every postcondition cites at least one of these. Numbered to match the footnotes above.
- [1]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid#uuidv4options-buffer-offset
- [2]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid#getrandomvalues-not-supported
- [3]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid/blob/main/src/rng-browser.ts
- [4]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid#uuidv7options-buffer-offset
- [5]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid/blob/main/src/v7.ts
- [6]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid#uuidvalidatestr
- [7]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid#uuidparsestr
- [8]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid/blob/main/src/parse.ts
- [9]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid#uuidversionstr
- [10]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid/blob/main/src/version.ts
- [11]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid#uuidv3name-namespace-buffer-offset
- [12]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid/blob/main/src/v35.ts
- [13]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid/blob/main/CHANGELOG.md#1400
- [14]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid#uuidv5name-namespace-buffer-offset
- [15]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid#uuidstringifyarr-offset
- [16]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid/blob/main/src/stringify.ts
- [17]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid#uuidv1options-buffer-offset
- [18]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid/blob/main/src/v1.ts
- [19]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid#uuidv6options-buffer-offset
- [20]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid/blob/main/src/v6.ts
- [21]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid#uuidv1tov6uuid
- [22]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid/blob/main/src/v1ToV6.ts
- [23]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid#uuidv6tov1uuid
- [24]github.com/uuidjs/uuidhttps://github.com/uuidjs/uuid/blob/main/src/v6ToV1.ts
Need a different package?
Request a profile