farion1231/cc-switch · critical · Error

crypto API not available - please update your operating syst

Error message

crypto API not available - please update your operating system

What it means

generateUUID first tries crypto.randomUUID, then builds a UUID v4 from crypto.getRandomValues. If globalThis.crypto exposes neither function, it throws - the runtime has no Web Crypto API at all. In a browser/webview this means an ancient engine (update the OS); in Node it means a pre-19 runtime without --experimental-global-webcrypto; in jsdom-based tests it usually means no crypto polyfill was installed.

Source

Thrown at src/utils/uuid.ts:20

 * 生成 UUID v4
 *
 * 优先使用 crypto.randomUUID(),不可用时使用 crypto.getRandomValues() 实现
 *
 * 兼容性:
 * - crypto.randomUUID(): Chrome 92+, Safari 15.4+, Firefox 95+
 * - crypto.getRandomValues(): Chrome 11+, Safari 5+, Firefox 21+
 */
export function generateUUID(): string {
  const cryptoApi = globalThis.crypto;

  // 优先使用原生 API
  if (typeof cryptoApi?.randomUUID === "function") {
    return cryptoApi.randomUUID();
  }

  // Fallback: 使用 crypto.getRandomValues 实现 UUID v4
  if (!cryptoApi?.getRandomValues) {
    throw new Error(
      "crypto API not available - please update your operating system",
    );
  }

  const bytes = new Uint8Array(16);
  cryptoApi.getRandomValues(bytes);

  // 设置版本 (4) 和变体 (RFC 4122)
  bytes[6] = (bytes[6] & 0x0f) | 0x40;
  bytes[8] = (bytes[8] & 0x3f) | 0x80;

  const hex = Array.from(bytes)
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");

  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
}

View on GitHub (pinned to a2e22f3302)

Solutions

  1. Polyfill before app/test boot: import { webcrypto } from 'node:crypto' and assign globalThis.crypto
  2. Upgrade to Node >= 19 (or pass --experimental-global-webcrypto on 15-18)
  3. Update the OS/WebView so the native crypto API is present

Example fix

// before (vitest/jest jsdom setup file)
// generateUUID() throws: crypto API not available

// after
import { webcrypto } from "node:crypto";
if (!globalThis.crypto) {
  globalThis.crypto = webcrypto as Crypto;
}
Defensive patterns

Strategy: fallback

Validate before calling

// Run once at boot / test setup
const hasWebCrypto =
  typeof globalThis.crypto?.randomUUID === "function" ||
  typeof globalThis.crypto?.getRandomValues === "function";

if (!hasWebCrypto) {
  const { webcrypto } = await import("node:crypto");
  globalThis.crypto = webcrypto as Crypto;
}

Type guard

function hasCryptoApi(c: unknown): c is Crypto {
  return (
    !!c &&
    (typeof (c as Crypto).randomUUID === "function" ||
      typeof (c as Crypto).getRandomValues === "function")
  );
}

Try / catch

try {
  id = generateUUID();
} catch (e) {
  if (e instanceof Error && e.message.includes("crypto API not available")) {
    throw new Error("This app needs a runtime with Web Crypto. Update the OS/browser or polyfill globalThis.crypto.");
  }
  throw e;
}

Prevention

When it happens

Trigger: generateUUID() under Node.js < 19 without the webcrypto flag, a jsdom test setup lacking globalThis.crypto, or a legacy embedded WebView without crypto support.

Common situations: Jest/Vitest jsdom environments; CI images pinned to old Node; older Linux WebKitGTK/Android WebView builds.

Related errors


AI-assisted analysis of farion1231/cc-switch@a2e22f3302 (2026-08-16). Data as JSON: /api/errors/a6a5ce85fefc791c. Report an issue: GitHub.