cube-js/cube · error · CubejsHandlerError

Invalid message format

Error message

Invalid message format

What it means

Any WebSocket message that is neither an authorization nor an unsubscribe message falls through to methodMessageSchema, which requires a valid 'method' field (one of the supported methods) plus method-specific payload. Zod failure throws CubejsHandlerError(400, 'Invalid message format') with a detail naming the missing/invalid field.

Source

Thrown at packages/cubejs-api-gateway/src/ws/subscription-server.ts:86

      if (!result.success) {
        throw new CubejsHandlerError(400, 'Invalid authorization message format', this.mapZodError(result.error));
      }

      return result.data;
    }

    if ('unsubscribe' in message) {
      const result = unsubscribeMessageSchema.safeParse(message);
      if (!result.success) {
        throw new CubejsHandlerError(400, 'Invalid unsubscribe message format', this.mapZodError(result.error));
      }

      return result.data;
    }

    const result = methodMessageSchema.safeParse(message);
    if (!result.success) {
      throw new CubejsHandlerError(400, 'Invalid message format', this.mapZodError(result.error));
    }

    return result.data;
  }

  public async processMessage(connectionId: string, body: string) {
    let message: any | undefined;

    try {
      message = this.deserializeMessage(body);
      message = this.validateMessage(message);

      await this.handleMessage(connectionId, message, false);
    } catch (e) {
      this.apiGateway.handleError({
        e,
        query: message?.query,
        res: this.resultFn(connectionId, message?.messageId, undefined, false),

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Send { messageId: '<id>', method: 'load'|'subscribe'|..., <methodPayload> } exactly as the schema requires; check methodMessageSchema for the version in use.
  2. Read the Zod detail after the error message to identify the exact missing/invalid field.
  3. Use @cubejs-client/core with the WebSocket transport instead of raw protocol messages.

Example fix

// before
socket.send(JSON.stringify({ method: 'fetch', query: {} }));
// after
socket.send(JSON.stringify({ messageId: '1', method: 'load', query: { measures: ['Orders.count'] } }));
Defensive patterns

Strategy: validation

Validate before calling

function isValidMethodMessage(msg: unknown): boolean {
  const m = msg as any;
  const METHODS = ['load', 'subscribe'];
  return typeof m === 'object' && m !== null &&
    typeof m.messageId === 'string' &&
    METHODS.includes(m.method);
}

Type guard

function isMethodMessage(m: unknown): m is { messageId: string, method: 'load' | 'subscribe', [k: string]: unknown } {
  return typeof m === 'object' && m !== null && typeof (m as any).method === 'string';
}

Try / catch

try {
  await handleMessage(frame);
} catch (e) {
  if (e.status === 400 && e.error === 'Invalid message format') {
    console.error('Method frame rejected:', e.message); // Zod path/message details
  } else throw e;
}

Prevention

When it happens

Trigger: Sending { ... } without a recognized method, misspelling it (e.g. 'Load' vs 'load'), omitting required fields like messageId or query, or including only unknown properties.

Common situations: Custom WS integrations guessing the protocol; sending REST-style payloads over WS; sending ping/heartbeat frames as regular messages; old client library talking to a newer server schema (or vice versa).

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/0f00eda7758a7d67. Report an issue: GitHub.