Skip to content

random.uuidv4: OperationError from crypto.getRandomValues in Firefox #108

@themiguelamador

Description

@themiguelamador

Problem

random.uuidv4() (and random.uint32()) can throw an unhandled OperationError (DOMException, code 0) when crypto.getRandomValues() fails transiently in Firefox.

We observed this in production on Firefox 148.0 / Ubuntu during Yjs Doc construction, which calls uuidv4() to generate the document GUID. The error is unrecoverable and crashes the application.

Stack trace (minified, from Yjs bundle)

OperationError: The operation failed for an operation-specific reason

Df  → crypto.getRandomValues.bind(crypto)   // lib0/webcrypto.js
Kb  → uuidv4()                              // lib0/random.js
t   → new Doc({ guid: uuidv4() })           // yjs

Reproduction

This is a transient failure — Firefox's Web Crypto implementation can occasionally throw OperationError from getRandomValues() due to process isolation or entropy pool issues. It's rare but not recoverable without a try/catch.

Suggested fix

Two complementary improvements:

1. Prefer crypto.randomUUID() for uuidv4()

All modern browsers (Firefox 95+, Chrome 92+, Safari 15.4+) support crypto.randomUUID(), which returns a UUID v4 string directly without going through getRandomValues. This avoids the failure entirely for the most common use case.

const uuidv4 = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
  ? () => crypto.randomUUID()
  : () => uuidv4Template.replace(/[018]/g, c => (c ^ uint32() & 15 >> c / 4).toString(16))

2. Add a try/catch fallback in uint32()

The module comment already says "falls back to Math.random if the browser does not support crypto", but this fallback isn't actually implemented for uint32(). A defensive fallback would protect all callers:

const uint32 = () => {
  try {
    return webcrypto.getRandomValues(new Uint32Array(1))[0]
  } catch {
    return (Math.random() * 2 ** 32) >>> 0
  }
}

Workaround

We're currently wrapping new Doc() in a retry loop on our side, which works since the failure is transient. But the fix belongs in lib0 since every Yjs consumer is affected.

Environment

  • lib0: 0.2.117
  • yjs: 13.6.14
  • Browser: Firefox 148.0 on Ubuntu
  • Error: OperationError (DOMException, code 0) from crypto.getRandomValues

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions