moeru-ai/airi · error · Error

No WebSocket constructor is available. Pass `wsConstructor`…

Error message

No WebSocket constructor is available. Pass `wsConstructor` or use a connector.

What it means

better-ws's built-in socket connector needs a WebSocket constructor: it uses options.wsConstructor when provided, otherwise globalThis.WebSocket. When neither exists — typical for Node.js where the global is absent or experimental — connect() throws this error telling you to pass wsConstructor or bring your own ClientConnector.

Solutions

  1. Install the ws package and pass it: createClient({ url, wsConstructor: WebSocket }) with import WebSocket from 'ws'
  2. Or run on Node 22+ where globalThis.WebSocket is available and enabled
  3. Or supply a custom ClientConnector via the connector option to fully own the transport (also needed for binary frames, since the built-in connector is text-only)

Example fix

// before
const client = createClient({ url: 'ws://localhost:3000' }) // Node: Error: No WebSocket constructor is available...

// after
import WebSocket from 'ws'
const client = createClient({ url: 'ws://localhost:3000', wsConstructor: WebSocket })
Defensive patterns

Strategy: validation

Validate before calling

// Resolve a constructor before creating the client
const wsConstructor = globalThis.WebSocket
  ?? (await import('ws')).default
if (!wsConstructor)
  throw new Error('No WebSocket implementation available in this runtime')
const client = createClient({ url, wsConstructor })

Prevention

When it happens

Trigger: Calling the client factory with only { url, protocols } in Node.js (globalThis.WebSocket undefined on Node < 22, or behind a flag on early 22 builds), or in a bundled/pruned environment where the global was stripped.

Common situations: Using a browser-oriented WS client during SSR or in a bot/script; CI running an older Node image; test environment (jsdom) without a WebSocket polyfill; custom runtimes lacking the WHATWG WebSocket.

Related errors


AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18). Data as JSON: /api/errors/4fa8987cd44f213c. Report an issue: GitHub.

Appendix: source

Thrown at packages/better-ws/src/client/index.ts:872

      waiters.add(wait.emit)

      void wait.promise.finally(() => {
        waiters.delete(wait.emit)
      }).catch(() => {})

      return wait.promise
    }
  }

  return client
}

function createSocketConnector(options: ClientUrlOptions): ClientConnector<string> {
  return {
    connect(events) {
      const WsConstructor = options.wsConstructor ?? globalThis.WebSocket
      if (!WsConstructor) {
        throw new Error('No WebSocket constructor is available. Pass `wsConstructor` or use a connector.')
      }

      const ws = new WsConstructor(options.url, options.protocols)
      return new Promise<ClientConnection<string>>((resolve, reject) => {
        let opened = false
        let failedBeforeOpen = false
        ws.onopen = () => {
          opened = true
          resolve({
            send: message => ws.send(message),
            close: (code, reason) => ws.close(code, reason),
          })
        }
        ws.onmessage = (event) => {
          if (typeof event.data === 'string') {
            events.message(event.data)
            return
          }

View on GitHub (pinned to 677329427f)