moeru-ai/airi · error · Error
Wrapped extension peer client does not support event listene
Error message
Wrapped extension peer client does not support event listeners.
What it means
createWebSocketExtensionPeer wraps an existing protocol client (server-sdk/src/extension-peer.ts). Its onEvent() forwards to this.client.onEvent, but only checks for the method at call time: if the wrapped client object does not implement onEvent (falsy), it throws this error rather than silently dropping the subscription. It signals you wrapped a send/close-only object and then tried to subscribe to events.
Source
Thrown at packages/server-sdk/src/extension-peer.ts:132
},
possibleEvents: input.possibleEvents ?? [],
permissions: input.permissions,
configSchema: input.configSchema,
dependencies: input.dependencies,
},
})
}
send(data: WebSocketEventOptionalSource<C>): boolean {
return this.client.send(data)
}
onEvent<E extends keyof WebSocketEvents<C>>(
event: E,
callback: (data: WebSocketBaseEvent<E, WebSocketEvents<C>[E]>) => void | Promise<void>,
): () => void {
if (!this.client.onEvent) {
throw new Error('Wrapped extension peer client does not support event listeners.')
}
return this.client.onEvent(event, callback)
}
close(): void {
this.client.close()
}
}
/** Creates a websocket extension peer over a server-sdk protocol client. */
export function createWebSocketExtensionPeer<C = undefined>(
options: WebSocketExtensionPeerOptions<C>,
): WebSocketExtensionPeer<C> {
return new WebSocketExtensionPeer(options)
}
View on GitHub (pinned to 677329427f)
Solutions
- Wrap the full client returned by createClient()/createWebSocketClient, which implements onEvent
- Upgrade the wrapped implementation to a version that exposes onEvent
- Feature-detect before subscribing: skip or degrade when `typeof client.onEvent !== 'function'`
Example fix
// before
createWebSocketExtensionPeer({ send: (d) => ws.send(d), close: () => ws.close() } as any).onEvent('x', cb)
// after
import { createWebSocketClient } from '@proj-airi/server-sdk'
createWebSocketExtensionPeer(createWebSocketClient({ url, events })).onEvent('x', cb) Defensive patterns
Strategy: type-guard
Validate before calling
if (!('onEvent' in client) || typeof client.onEvent !== 'function')
throw new Error('Cannot subscribe: wrap a full client that implements onEvent') Type guard
type WithEventListeners<T> = T & { onEvent: (event: string, cb: (data: unknown) => void) => () => void }
function supportsEventListeners<T extends { onEvent?: unknown }>(client: T): client is T & WithEventListeners<T> {
return typeof client.onEvent === 'function'
} Try / catch
try {
peer.onEvent('player-heartbeat-set', handler)
}
catch (error) {
if (error instanceof Error && error.message.includes('does not support event listeners')) {
// degrade: run without live events instead of crashing startup
return
}
throw error
} Prevention
- Wrap the client returned by createClient, not hand-rolled stubs
- Feature-detect onEvent before subscribing when adapters may vary
- In tests, mock onEvent so the peer exercises the real subscription path
When it happens
Trigger: Passing a hand-rolled or mock client that implements only send()/close(), wrapping an older server-sdk client version from before onEvent existed, or a custom transport adapter that never exposed event listeners.
Common situations: Tests with fake clients, adapter layers that translate another RPC lib into the client interface, or version mismatches after a partial monorepo upgrade.
Related errors
- Client is not connected, current status: ${this.status}
- Invalid AIRI websocket message.
- No WebSocket constructor is available. Pass `wsConstructor`
- WebSocket transport is not implemented for node runtime yet.
- WebSocket transport is not implemented for web runtime yet.
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/071d750ef5c5ac85.
Report an issue: GitHub.