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

  1. Inspect `error.source` and `error.cause` (valibot issues) to see which field failed, then fix the producer.
  2. Ensure the peer emits valid envelopes via `stringifyEvent({ type, data })` with `data` as a non-array object.
  3. Align client and server on the same protocol/schema version.
  4. 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

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


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