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
- JSON.stringify the message before sending: ws.send(JSON.stringify({ messageId: '1', ... })).
- Validate the payload with JSON.parse locally to catch malformed JSON before transmitting.
- Log the exact message text and the parse error (in the error reason) to find the malformed frame.
- 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
- Always JSON.stringify payloads before ws.send.
- Avoid circular structures and BigInt/undefined values that break stringify semantics.
- Test WS payloads with a script that validates JSON first.
- Beware proxies/compression middleware that could truncate frames.
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid authorization message format
- Invalid unsubscribe message format
- Invalid message format
- messageId is required
- Method is required
AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02).
Data as JSON: /api/errors/ea12a51ca5c1edef.
Report an issue: GitHub.