cube-js/cube · error · CubejsHandlerError

Invalid JSON payload

Error message

Invalid JSON payload

What it means

The WebSocket subscription server expects every incoming frame body to be JSON. deserializeMessage runs JSON.parse on the raw message, and on parse failure it throws CubejsHandlerError(400, 'Invalid JSON payload') carrying the underlying parser error as the reason.

Source

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

    protected readonly contextAcceptor: ContextAcceptorFn,
  ) {
  }

  protected resultFn(connectionId: string, messageId: string | undefined, requestId: string | undefined, logNetworkUsage: boolean = true) {
    return async (message, { status } = { status: 200 }) => {
      if (logNetworkUsage) {
        this.apiGateway.log({ type: 'Outgoing network usage', service: 'api-ws', bytes: calcMessageLength(message), }, { requestId });
      }

      return this.sendMessage(connectionId, { messageId, message, status });
    };
  }

  protected deserializeMessage(message: any): any {
    try {
      return JSON.parse(message);
    } catch (e: any) {
      throw new CubejsHandlerError(400, 'Invalid JSON payload', e.message);
    }
  }

  protected mapZodError(error: ZodError): string {
    return error.issues
      .map(e => (e.path.length ? `${e.path.join('.')}: ${e.message}` : e.message))
      .join(', ');
  }

  protected validateMessage(message: object): WsMessage {
    if ('authorization' in message) {
      const result = authMessageSchema.safeParse(message);
      if (!result.success) {
        throw new CubejsHandlerError(400, 'Invalid authorization message format', this.mapZodError(result.error));
      }

      return result.data;
    }

View on GitHub (pinned to 7d981676b3)

Solutions

  1. JSON.stringify the message before sending: ws.send(JSON.stringify({ messageId: '1', ... })).
  2. Validate the payload with JSON.parse locally to catch malformed JSON before transmitting.
  3. Log the exact message text and the parse error (in the error reason) to find the malformed frame.
  4. Check middleware/proxies that might truncate or transform frames.

Example fix

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

Strategy: validation

Validate before calling

function safeWsSend(socket, payload) {
  const text = JSON.stringify(payload);
  JSON.parse(text); // round-trip check
  socket.send(text);
}

Type guard

function isSerializableJson(v: unknown): boolean {
  try { JSON.parse(JSON.stringify(v)); return true; } catch { return false; }
}

Try / catch

try {
  await transport.sendMessage(raw);
} catch (e) {
  if (e.status === 400 && e.error === 'Invalid JSON payload') {
    console.error('Sent non-JSON frame:', raw);
  } else throw e;
}

Prevention

When it happens

Trigger: Sending a raw string over the WebSocket that is not valid JSON — unquoted text, trailing commas, single quotes, HTML, or a truncated frame; also messages that are empty or binary payloads.

Common situations: Hand-rolled WebSocket clients doing ws.send('load') instead of JSON.stringify({...}); curl/echo testing tools sending plain text; intermediate proxies or compression layers corrupting frames; JSON.stringify omitted in the client library glue code.

Understand the failure class

Related errors


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