moeru-ai/airi · error · InvalidMessageError
Invalid AIRI websocket message.
Error message
Invalid AIRI websocket message.
What it means
Thrown as an `InvalidMessageError` by `parseEvent()` in the server-sdk codec when the parsed candidate fails valibot `safeParse` against the AIRI event envelope schema (`{ type: string, data: non-array object }`). The SuperJSON parse may have succeeded, but the resulting shape was not a valid envelope; the error carries the valibot issues as `cause` and the candidate as `source`. The fixed message string is `'Invalid AIRI websocket message.'`.
Source
Thrown at packages/server-sdk/src/codec.ts:58
/** Parses one AIRI websocket protocol event from SuperJSON or plain JSON text. */
export function parseEvent<C = undefined>(text: string): WebSocketEvent<C> {
let superJsonParsed: WebSocketEvent<C> | undefined
let superJsonError: unknown
try {
superJsonParsed = parse<WebSocketEvent<C>>(text)
}
catch (error) {
superJsonError = error
}
const potentialEvent = superJsonParsed && typeof superJsonParsed === 'object' && 'type' in superJsonParsed
? superJsonParsed
: parsePlainJson(text, superJsonError)
const result = safeParse(eventEnvelopeSchema, potentialEvent)
if (!result.success) {
throw new InvalidMessageError({ cause: result.issues, source: potentialEvent })
}
return potentialEvent as WebSocketEvent<C>
}
/** Serializes one AIRI websocket protocol event with SuperJSON. */
export function stringifyEvent<C = undefined>(
event: WebSocketBaseEvent<string, unknown> | WebSocketEvent<C>,
) {
return stringify(event)
}
function parsePlainJson(text: string, superJsonError: unknown): unknown {
try {
return JSON.parse(text)
}
catch (jsonError) {
throw new InvalidMessageError({View on GitHub (pinned to 27111382b4)
Solutions
- Inspect `error.source` and `error.cause` (valibot issues) to see which field failed, then fix the producer.
- Ensure the peer emits valid envelopes via `stringifyEvent({ type, data })` with `data` as a non-array object.
- Align client and server on the same protocol/schema version.
- Wrap `parseEvent` in a guard that drops/acks invalid messages rather than crashing the client.
Example fix
// before
const event = parseEvent(text)
// after
try {
const event = parseEvent(text)
} catch (e) {
if (e instanceof InvalidMessageError) {
console.warn('dropping invalid message', e.source, e.cause)
return
}
throw e
} Defensive patterns
Strategy: try-catch
Validate before calling
import { InvalidMessageError, parseEvent } from '@proj-airi/server-sdk/codec'
function safeParseEvent(text: string) {
try {
return { event: parseEvent(text), error: undefined }
} catch (e) {
if (e instanceof InvalidMessageError) return { event: undefined, error: e }
throw e
}
} Type guard
function isAiriEnvelope(value: unknown): value is { type: string; data: object } {
return Boolean(value)
&& typeof value === 'object'
&& !Array.isArray(value)
&& typeof (value as any).type === 'string'
&& Boolean((value as any).data)
&& typeof (value as any).data === 'object'
&& !Array.isArray((value as any).data)
} Try / catch
try {
const event = parseEvent(text)
handle(event)
} catch (e) {
if (e instanceof InvalidMessageError) {
log.warn('dropping invalid message', { source: e.source, cause: e.cause })
return
}
throw e
} Prevention
- Inspect error.source and error.cause to find the failing envelope field.
- Ensure peers emit valid { type, data } envelopes via stringifyEvent.
- Keep client and server on the same protocol version.
- Wrap parseEvent and drop/log invalid messages rather than crashing the client.
When it happens
Trigger: Receiving a websocket message whose SuperJSON-decoded (or plain-JSON-decoded) form is not `{ type, data }` — e.g. missing `type`, `data` is an array or primitive, or extra-non-rest issues. Typically hits a client when the server (or another peer) emits a non-conformant or different-protocol message.
Common situations: Server/peer running a different protocol version; a malformed event emitted by buggy server code; a third-party bridge sending JSON arrays or primitive payloads; test fixtures with invalid envelope shapes.
Related errors
- Invalid WebSocket event format.
- streaming-tts: not authenticated
- AIRI host websocket bridge is unavailable
- No candidate server channel URL was reachable. ${errors.join
- AIRI host websocket bridge is unavailable
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/2eacd26411fc44c9.
Report an issue: GitHub.