Error [TypeError: crypto.randomUUID is not a function]

TypeError: crypto.randomUUID is not a function — fixing a missing randomUUID on older Node

FIX SUMMARY verified
Applies when
node:14.16.1-alpinecall to crypto.randomUUID on Node < 14.17

Verified: reproduced in node:14.16.1-alpine, then the TypeError: crypto.randomUUID is not a function signature was gone after the fix (exit 0).

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 function
  • crypto.randomUUID is not a function
  • randomUUID 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 versioncrypto.randomUUIDBehaviour
14.16.x and earlierabsentTypeError: crypto.randomUUID is not a function
14.17.0 and later in the 14 linepresentworks as written
15.0.0 – 15.5.xabsentTypeError: crypto.randomUUID is not a function
15.6.0 and later in the 15 line / 16 and laterpresentworks 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 function versus is not defined: crypto.randomUUID is a method on the crypto object, so its absence reads is not a function. Globals such as fetch and structuredClone instead give ReferenceError: ... is not defined when absent. The endings differ, but both are the same version boundary — that version does not have that API yet. For fetch see Fixing ReferenceError: fetch is not defined, and for structuredClone see Fixing ReferenceError: structuredClone is not defined.
  • It failed with crypto undefined: if crypto itself is missing — Cannot read properties of undefined (reading 'randomUUID') — that is not a version boundary. Most likely the require('crypto') (or import in ESM) is missing, or you are expecting Node’s crypto in a browser environment.
  • crypto.randomUUID is missing in the browser (Web Crypto): the browser’s crypto.randomUUID is only available in a secure context. Secure contexts include http://localhost, 127.0.0.1 and file: besides https, so it can work in local development and be undefined in production served over http. This article is about running under Node, but if you hit it in the browser, check that you are serving over https.

Verification (machine-verified)

This fix was reproduced inside the version-pinned node:14.16.1-alpine container, and the TypeError: crypto.randomUUID is not a function signature was then machine-checked to be gone after the fix.

verify — run-case.mjs
$ node run-case.mjs node/crypto-randomuuid-not-a-function
● reproduce TypeError: crypto.randomUUID is not a function present ✓
● apply fix exit 0
● re-run TypeError: crypto.randomUUID is not a function gone ✓
PASS verified · node:14.16.1-alpine · signature gone

That check covers the image above and nothing else. If the fix did not work in your environment, or something here is wrong, send a report (the article and the verified image go into the subject and body).