moeru-ai/airi · error

No WebSocket constructor is available. Pass `wsConstructor`

Error message

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

What it means

The socket connector resolves the WebSocket constructor as options.wsConstructor ?? globalThis.WebSocket. If both are undefined it refuses to proceed because there is no way to open a socket. Browsers define globalThis.WebSocket natively, but many non-browser runtimes (Node without the `ws` package, custom embeddings, some test/SSR environments) do not.

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 27111382b4)

Solutions

  1. Install `ws` and pass wsConstructor: import WebSocket from 'ws'; new Client({ url, wsConstructor: WebSocket }).
  2. Use a custom ClientConnector (e.g. a node ws connector) instead of the default socket connector.
  3. Polyfill globalThis.WebSocket in the test/SSR environment.
  4. Detect runtime support and skip the connection when no constructor is available.

Example fix

// before
const client = createClient({ url: 'wss://example.com' })
// in Node: throws 'No WebSocket constructor is available...'

// after
import WebSocket from 'ws'
const client = createClient({ url: 'wss://example.com', wsConstructor: WebSocket as any })
Defensive patterns

Strategy: validation

Validate before calling

function resolveWsConstructor(): typeof WebSocket | undefined {
  return options.wsConstructor ?? (typeof globalThis !== 'undefined' ? globalThis.WebSocket : undefined)
}
if (!resolveWsConstructor()) {
  throw new Error('Install \'ws\' and pass wsConstructor, or provide a custom connector')
}

Type guard

function isWsConstructor(fn: unknown): fn is new (url: string, protocols?: string | string[]) => WebSocket {
  return typeof fn === 'function'
}

Try / catch

try {
  return createClient({ url, wsConstructor: WebSocketCtor })
}
catch (err) {
  if (/No WebSocket constructor/i.test((err as Error).message)) {
    // switch to a custom connector or polyfill globalThis.WebSocket
    throw new Error('Provide wsConstructor (e.g. from \'ws\') for this runtime')
  }
  throw err
}

Prevention

When it happens

Trigger: Using the better-ws client in Node.js without installing/importing `ws`; running in an SSR or test environment where globalThis.WebSocket is undefined; targeting a runtime that exposes WebSocket under a different global; the user passed a falsy wsConstructor.

Common situations: Server-side subscriptions, integration tests in jsdom (which lacks WebSocket unless polyfilled), Deno/bun-detection differences, or a bundler that stripped the WebSocket reference.

Related errors


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