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
- Install `ws` and pass wsConstructor: import WebSocket from 'ws'; new Client({ url, wsConstructor: WebSocket }).
- Use a custom ClientConnector (e.g. a node ws connector) instead of the default socket connector.
- Polyfill globalThis.WebSocket in the test/SSR environment.
- 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
- In Node, always pass wsConstructor from the `ws` package.
- Feature-detect globalThis.WebSocket before relying on the default connector.
- Polyfill WebSocket in jsdom/SSR test environments.
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
- WebSocket transport is not implemented for node runtime yet.
- AIRI host websocket bridge is unavailable
- No candidate server channel URL was reachable. ${errors.join
- AIRI host websocket bridge is unavailable
- Godot stage bridge is not connected.
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/4fa8987cd44f213c.
Report an issue: GitHub.