TypeError: crypto.randomUUID is not a function — Every Real Cause and Fix

Published 22 Sep 2026 · 8 min read

Unlike a malformed-input error, crypto.randomUUID is not a function isn't about what you passed in — it's about which crypto object your code landed on, and whether it has that method at all. That makes it easy to misdiagnose: it shows up in production but never in local dev, or on one teammate's machine but not another's, or only after a dependency update. Every cause below was reproduced for real — live in a headless Chromium build for the browser cases, and in Node for the server-side one — rather than assumed from an old GitHub issue, with a live self-check and a working fallback in this site's own UUID & ULID Lab.

Quick answer: crypto.randomUUID() is a secure-context-only Web API — the browser simply never defines it on a plain http:// origin that isn't localhost, exactly like crypto.subtle. It also isn't available on browsers older than Safari 15.4 / Chrome 92 / Firefox 95, or in Node.js before v14.17.0. Calling a method that doesn't exist on an object throws TypeError: crypto.randomUUID is not a function — the same generic "not a function" message you'd get from calling any missing method, which is why this specific error is really four different root causes wearing one costume.

Quick reference

Every result below was checked directly — the browser cases against a real headless Chromium build (via Playwright, with a genuine non-localhost http:// origin, not a hypothetical), the Node case by running Node itself — so "throws" and "works fine" both reflect real, current behavior.

SymptomCauseFix
Works on localhost, fails on your LAN IP or a staging boxPlain HTTP origin that isn't localhost/127.0.0.1 — not a secure contextServe over HTTPS, or feature-detect and fall back to crypto.getRandomValues()
Fails only for some users, on older devices or browsersBrowser predates randomUUID support (before Safari 15.4 / Chrome 92 / Firefox 95)Feature-detect; don't assume support based on "modern browser"
Fails in a build script, SSR render, or CI job, not in the browserNode.js older than v14.17.0 — the method didn't exist on the crypto module yetUpgrade Node, or use crypto.randomBytes() to build a v4 UUID by hand
Fails after adding an unrelated import, in one file onlyA local crypto variable (often from crypto-js or similar) is shadowing the real Web Crypto globalRename the import, or reference window.crypto.randomUUID() explicitly

1. An insecure (plain HTTP) origin

crypto.randomUUID() is gated behind the same secure context requirement as crypto.subtle: browsers only expose it to pages loaded over HTTPS, or over plain HTTP from localhost/127.0.0.1 specifically, since that carve-out is what lets local development work without a certificate. Load the same code from any other plain http:// origin — a LAN IP, an internal tool served over HTTP, a staging box without TLS — and crypto.randomUUID isn't a broken function, it's undefined.

// Verified live in a real Chromium build, navigated to a genuine
// non-localhost http:// origin (not a hypothetical):

window.isSecureContext        // false
typeof crypto.randomUUID      // "undefined"
typeof crypto.subtle          // "undefined"  (same restriction)
typeof crypto.getRandomValues // "function"   (this one still works — see the fix below)

crypto.randomUUID();
// TypeError: crypto.randomUUID is not a function

// The exact same code on an https:// origin:
window.isSecureContext   // true
crypto.randomUUID()      // "fb0a9986-9381-47ab-a545-6a47fdebdbb7"

This is the single most common way teams hit this error in the wild — it works for every developer on localhost, passes code review, then throws the moment someone opens the same build on an HTTP-only internal server or tests it from a phone over the LAN.

Check your own environment

Open UUID & ULID Lab — it runs this exact check live and shows whether crypto.randomUUID is available in your current browser and origin. Since this site itself is served over HTTPS, you'll see "Available" here regardless; the value is running the same three lines against your own app's console.

Open the live Environment Check

2. A browser or Node.js version that predates the method

crypto.randomUUID() is a relatively recent addition. It isn't available before Safari 15.4 (March 2022), Chrome/Edge 92 (July 2021), or Firefox 95 (December 2021) — documented in MDN's compatibility table. On the server, Node.js only added it to the crypto module in v14.17.0 (and to the Web Crypto-style globalThis.crypto in v19). A user on an old Safari release, or a CI image pinned to an older Node LTS, hits the exact same "not a function" error — with a perfectly valid secure context, which is what makes it easy to misdiagnose as cause #1.

// Reproduced in Node directly: simulating a pre-v14.17 crypto module,
// where randomUUID genuinely doesn't exist as a property yet.
const crypto = require('crypto');
delete crypto.randomUUID;

crypto.randomUUID();
// TypeError: crypto.randomUUID is not a function

Feature-detecting with typeof crypto.randomUUID === 'function' before calling it catches both this and cause #1 with the same check — which is exactly what the fix at the bottom of this page does.

3. A shadowed crypto variable

This one has nothing to do with browser support. If a file imports or declares its own crypto — most often import crypto from 'crypto-js', or a locally scoped helper object literally named crypto — that local binding shadows the real Web Crypto global for the rest of that module's scope. Calling .randomUUID() on it fails for the same reason it would on any object that never had the method: it simply isn't there.

// Reproduced directly: any object bound to the name "crypto" that isn't
// the real Web Crypto API produces the identical error text.
function demo() {
  // Stand-in for `import crypto from 'crypto-js';` or any other
  // non-Web-Crypto package/variable named "crypto" shadowing the global.
  const crypto = { AES: {}, SHA256: () => {} };

  crypto.randomUUID();
}
demo();
// TypeError: crypto.randomUUID is not a function

This is the cause to suspect when the error appears suddenly in one specific file right after adding an encryption or hashing library, while the exact same call works fine everywhere else in the codebase. The fix is just a rename — import CryptoJS from 'crypto-js' instead of import crypto from 'crypto-js' — or referencing window.crypto.randomUUID() explicitly so the real global can't be shadowed.

The fix: feature-detect, then fall back to getRandomValues()

The one Web Crypto method that isn't secure-context-gated is crypto.getRandomValues() — verified live on the same insecure origin from cause #1 above, where randomUUID and subtle were both undefined:

// Same insecure http:// origin as the reproduction above:
const bytes = new Uint8Array(16);
crypto.getRandomValues(bytes);  // works — no secure-context requirement
typeof crypto.randomUUID;       // still "undefined"

That makes it a reliable fallback for building a v4 UUID by hand — set the version nibble to 4 and the variant nibble to 8–b per RFC 4122, then format as the standard 8-4-4-4-12 hex string:

function generateUuidV4Fallback() {
  const bytes = new Uint8Array(16);
  crypto.getRandomValues(bytes);
  bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4
  bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10xx
  const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0'));
  return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex.slice(6, 8).join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10, 16).join('')}`;
}

function generateUuidV4() {
  return typeof crypto.randomUUID === 'function'
    ? crypto.randomUUID()
    : generateUuidV4Fallback();
}

This is exactly what this site's own UUID & ULID Lab now does — it previously called crypto.randomUUID() directly with no fallback, which meant clicking "Generate" on an unsupported browser or an insecure origin failed silently. It's fixed there now, and you can try the fallback path itself without needing a genuinely broken environment:

Try the fallback path live

This link flips on a "Simulate crypto.randomUUID() unavailable" toggle and generates through the getRandomValues() fallback above instead of the native method — so you can see it producing valid, correctly-formatted UUID v4s exactly as it would on an old browser or an HTTP-only origin.

Open UUID & ULID Lab with the fallback demo on

Debugging checklist

  1. Open the console and check window.isSecureContext. If it's false, that's cause #1 — you're not on HTTPS or localhost.
  2. If isSecureContext is true but typeof crypto.randomUUID is still "undefined", it's cause #2 — an old browser (client-side) or an old Node.js version (server-side/build tooling).
  3. If typeof crypto.randomUUID is "function" in the console but the error still happens inside your app, log crypto itself at the top of the failing file — a shadowed local variable (cause #3) is almost always the reason console and app disagree.
  4. Either way, feature-detect before calling it and fall back to crypto.getRandomValues(), which works in every context — don't gate a whole feature on randomUUID alone.

Related to this: crypto.subtle (used for hashing via crypto.subtle.digest() in tools like Hash Master) is gated behind the exact same secure-context requirement as randomUUID — an insecure origin loses both at once, which is a useful tell when you're not sure which cause you're looking at.

🆔 Generate UUIDs, ULIDs and Nano IDs — with a fallback that never crashes

UUID & ULID Lab runs entirely in your browser, checks your environment live, and now falls back automatically instead of throwing if crypto.randomUUID() isn't available. Nothing you generate is ever sent anywhere.

Open UUID & ULID Lab →

← Back to Blog · See also: InvalidCharacterError: atob →