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
- Polyfill before app/test boot: import { webcrypto } from 'node:crypto' and assign globalThis.crypto
- Upgrade to Node >= 19 (or pass --experimental-global-webcrypto on 15-18)
- 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
- Install the webcrypto polyfill in jsdom test setups before importing app code
- Run CI on Node >= 19 so globalThis.crypto exists natively
- Probe crypto support at startup and fail fast with an upgrade hint
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.