{"record":{"id":"2eacd26411fc44c9","repo":"moeru-ai/airi","slug":"invalid-airi-websocket-message","errorCode":null,"errorMessage":"Invalid AIRI websocket message.","messagePattern":"Invalid AIRI websocket message\\.","errorType":"validation","errorClass":"InvalidMessageError","httpStatus":null,"severity":"error","filePath":"packages/server-sdk/src/codec.ts","lineNumber":58,"sourceCode":"/** Parses one AIRI websocket protocol event from SuperJSON or plain JSON text. */\nexport function parseEvent<C = undefined>(text: string): WebSocketEvent<C> {\n  let superJsonParsed: WebSocketEvent<C> | undefined\n  let superJsonError: unknown\n\n  try {\n    superJsonParsed = parse<WebSocketEvent<C>>(text)\n  }\n  catch (error) {\n    superJsonError = error\n  }\n\n  const potentialEvent = superJsonParsed && typeof superJsonParsed === 'object' && 'type' in superJsonParsed\n    ? superJsonParsed\n    : parsePlainJson(text, superJsonError)\n\n  const result = safeParse(eventEnvelopeSchema, potentialEvent)\n  if (!result.success) {\n    throw new InvalidMessageError({ cause: result.issues, source: potentialEvent })\n  }\n\n  return potentialEvent as WebSocketEvent<C>\n}\n\n/** Serializes one AIRI websocket protocol event with SuperJSON. */\nexport function stringifyEvent<C = undefined>(\n  event: WebSocketBaseEvent<string, unknown> | WebSocketEvent<C>,\n) {\n  return stringify(event)\n}\n\nfunction parsePlainJson(text: string, superJsonError: unknown): unknown {\n  try {\n    return JSON.parse(text)\n  }\n  catch (jsonError) {\n    throw new InvalidMessageError({","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/packages/server-sdk/src/codec.ts#L40-L76","documentation":"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.'`.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst event = parseEvent(text)\n\n// after\ntry {\n  const event = parseEvent(text)\n} catch (e) {\n  if (e instanceof InvalidMessageError) {\n    console.warn('dropping invalid message', e.source, e.cause)\n    return\n  }\n  throw e\n}","handlingStrategy":"try-catch","validationCode":"import { InvalidMessageError, parseEvent } from '@proj-airi/server-sdk/codec'\n\nfunction safeParseEvent(text: string) {\n  try {\n    return { event: parseEvent(text), error: undefined }\n  } catch (e) {\n    if (e instanceof InvalidMessageError) return { event: undefined, error: e }\n    throw e\n  }\n}","typeGuard":"function isAiriEnvelope(value: unknown): value is { type: string; data: object } {\n  return Boolean(value)\n    && typeof value === 'object'\n    && !Array.isArray(value)\n    && typeof (value as any).type === 'string'\n    && Boolean((value as any).data)\n    && typeof (value as any).data === 'object'\n    && !Array.isArray((value as any).data)\n}","tryCatchPattern":"try {\n  const event = parseEvent(text)\n  handle(event)\n} catch (e) {\n  if (e instanceof InvalidMessageError) {\n    log.warn('dropping invalid message', { source: e.source, cause: e.cause })\n    return\n  }\n  throw e\n}","preventionTips":["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."],"tags":["server-sdk","websocket","codec","validation","superjson"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}