Calling crypto.randomUUID() to generate a UUID can fail on older Node with this error.
TypeError: crypto.randomUUID is not a function
at Object.<anonymous> (/app/index.js:3:20)
The wording varies in what people paste into a search box. All of these are the same cause.
TypeError: crypto.randomUUID is not a functioncrypto.randomUUID is not a functionrandomUUID is not a function
Since crypto itself clearly loaded and only the method is missing, it is tempting to suspect the require / import, but the cause is not module loading. It is that crypto.randomUUID was added in Node 14.17.0 and does not exist before that. The crypto module is present in every version; only the randomUUID method is absent from older ones, which is why this reads is not a function rather than is not defined.
This is a textbook version boundary, and the boundary sits inside a single major version, between 14.16 and 14.17. So even when you believe “it works on Node 14”, the result depends on the minor version you pinned. It shows up as: fine on your development machine and on newer CI, failing only in a container pinned to something like FROM node:14.16.
From which version is crypto.randomUUID available
crypto.randomUUID (which returns an RFC 4122 version 4 UUID) was added in Node 14.17.0, and reached the 15 line in 15.6.0. Every version from 16 on has it.
| Node version | crypto.randomUUID | Behaviour |
|---|---|---|
| 14.16.x and earlier | absent | TypeError: crypto.randomUUID is not a function |
| 14.17.0 and later in the 14 line | present | works as written |
| 15.0.0 – 15.5.x | absent | TypeError: crypto.randomUUID is not a function |
| 15.6.0 and later in the 15 line / 16 and later | present | works as written |
Because the addition was backported to LTS (14) first, the 14 line had it from 14.17.0 while the then-current 15 line did not get it until 15.6.0. That produces an inversion: 15.0 through 15.5 carry a higher number than 14.17 and yet have no crypto.randomUUID. Judge by “14.17 or later in the 14 line / 15.6 or later / 16 or later”, not by which number is bigger. This shape — landing in LTS first and inverting the numbers — is exactly the one in crypto.hash is not a function. If you pin something like node:14.16.1, or an old image is still in your cache, you stop on the wrong side of this boundary.
Reproduction (minimal)
Put down this one file.
// index.js
const crypto = require('crypto');
console.log(crypto.randomUUID());
Run node index.js on Node 14.16 or earlier and you get TypeError: crypto.randomUUID is not a function with exit code 1. On Node 14.17 or later it prints a UUID string (something shaped like 3b12f1df-...). Same code, same machine — only the Node minor version changes the result.
The fix
1. Move Node to 14.17 or later (in practice, an 18-or-later LTS)
If you want to keep using crypto.randomUUID, raise the Node that runs it, including your CI and your production Docker base image. The API landed in 14.17.0, but 14, 15 and 16 are all end-of-life, so for production line up on an LTS that is supported at the time. Check the pinned tag in your Dockerfile and the version setting in CI.
2. Use the uuid package (when older Node has to stay)
If older Node stays in your support matrix, use uuid, the widely used package for this. It returns the same version 4 UUID that crypto.randomUUID does.
// index.js (using the uuid package)
const { v4: uuidv4 } = require('uuid');
console.log(uuidv4());
3. Build it yourself from crypto.randomBytes (when you don’t want another dependency)
If you would rather not add a package, you can assemble a version 4 UUID from crypto.randomBytes, which every version has. The bit twiddling sets the RFC 4122 version (the third group starts with 4) and variant (the fourth group starts with 8 through b) — the 13th and 17th characters of the hyphen-free hex.
const crypto = require('crypto');
function randomUuid() {
if (typeof crypto.randomUUID === 'function') {
return crypto.randomUUID();
}
const bytes = crypto.randomBytes(16);
bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4
bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant
const hex = bytes.toString('hex');
return [
hex.slice(0, 8),
hex.slice(8, 12),
hex.slice(12, 16),
hex.slice(16, 20),
hex.slice(20),
].join('-');
}
console.log(randomUuid());
crypto.randomBytes returns cryptographically secure random bytes, so the UUIDs this produces are as good as crypto.randomUUID’s. Examples that build UUIDs from Math.random() circulate widely; don’t use them, because the quality of that randomness is not guaranteed.
Similar symptoms with a different cause
is not a functionversusis not defined:crypto.randomUUIDis a method on thecryptoobject, so its absence readsis not a function. Globals such asfetchandstructuredCloneinstead giveReferenceError: ... is not definedwhen absent. The endings differ, but both are the same version boundary — that version does not have that API yet. Forfetchsee Fixing ReferenceError: fetch is not defined, and forstructuredClonesee Fixing ReferenceError: structuredClone is not defined.- It failed with
cryptoundefined: ifcryptoitself is missing —Cannot read properties of undefined (reading 'randomUUID')— that is not a version boundary. Most likely therequire('crypto')(orimportin ESM) is missing, or you are expecting Node’scryptoin a browser environment. crypto.randomUUIDis missing in the browser (Web Crypto): the browser’scrypto.randomUUIDis only available in a secure context. Secure contexts includehttp://localhost,127.0.0.1andfile:besideshttps, so it can work in local development and beundefinedin production served overhttp. This article is about running under Node, but if you hit it in the browser, check that you are serving over https.