{"record":{"id":"a46d09ffdb8c0b02","repo":"hasura/graphql-engine","slug":"unable-to-parse-websocket-message-0","errorCode":null,"errorMessage":"Unable to parse WebSocket message: {0}","messagePattern":"Unable to parse WebSocket message: (.+?)","errorType":"exception","errorClass":"ParseError","httpStatus":null,"severity":"error","filePath":"v3/crates/graphql/graphql-ws/src/websocket/tasks.rs","lineNumber":166,"sourceCode":"            )\n            .await\n            .into_inner();\n        if break_loop == BreakLoop::Break {\n            break;\n        }\n    }\n}\n\nenum ParsedClientMessage {\n    Close,\n    Protocol(protocol::types::ClientMessage),\n}\n\n#[derive(thiserror::Error, Debug)]\nenum ParseError {\n    #[error(\"Unable to fetch message from WebSocket: {0}\")]\n    WebSocket(#[from] axum::Error),\n    #[error(\"Unable to parse WebSocket message: {0}\")]\n    Json(#[from] serde_json::Error),\n}\n\nimpl tracing_util::TraceableError for ParseError {\n    fn visibility(&self) -> tracing_util::ErrorVisibility {\n        tracing_util::ErrorVisibility::User\n    }\n}\n\nfn parse_incoming_message(\n    message: Result<ws::Message, axum::Error>,\n) -> Result<ParsedClientMessage, ParseError> {\n    let tracer = tracing_util::global_tracer();\n    tracer.in_span(\n        \"parse_incoming_message\",\n        \"Parse WebSocket message frame\",\n        tracing_util::SpanVisibility::User,\n        || {","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/v3/crates/graphql/graphql-ws/src/websocket/tasks.rs#L148-L184","documentation":"This variant of ParseError wraps a serde_json::Error raised when a received WebSocket text message cannot be deserialized into the graphql-ws ClientMessage type. It means the frame was valid WebSocket but not a valid graphql-ws protocol message (bad JSON or wrong shape). The server cannot interpret the message and reports this error.","triggerScenarios":"A client sends a JSON message that doesn't match any ClientMessage variant of the graphql-ws protocol — e.g. unknown message 'type', missing required fields, or a protocol-version mismatch producing different message shapes.","commonSituations":"Hand-rolled WebSocket clients sending arbitrary JSON; clients implementing a different GraphQL subscription protocol (subscriptions-transport-ws) against a graphql-ws server; version skew between client and server protocol implementations.","solutions":["Validate outgoing frames against the graphql-ws message spec (type must be one of connection_initsubscribe... with required fields per message)","Use a maintained client library (graphql-ws npm package) matching the server protocol version instead of hand-writing messages","Capture the offending frame and compare its shape with the protocol's ClientMessage definitions"],"exampleFix":"// before (client sends invalid frame)\nws.send(JSON.stringify({ id: '1', type: 'start', payload: { query } }));\n\n// after\nws.send(JSON.stringify({ id: '1', type: 'subscribe', payload: { query } }));","handlingStrategy":"validation","validationCode":"const VALID_TYPES = new Set(['connection_init','ping','pong','subscribe','complete']);\nfunction isValidClientMessage(msg: unknown): boolean {{\n  const m = msg as {{ type?: string }};\n  return typeof m?.type === 'string' && VALID_TYPES.has(m.type);\n}}\nif (!isValidClientMessage(parsed)) ws.send(JSON.stringify({{ type: 'ping' }}));","typeGuard":"function isClientMessage(v: unknown): v is {{ type: string; [k: string]: unknown }} {{\n  return typeof v === 'object' && v !== null && typeof (v as any).type === 'string';\n}}","tryCatchPattern":null,"preventionTips":["Validate frames against the graphql-ws message spec before sending","Use a protocol-conformant client library instead of hand-rolled sends","Pin client and server to compatible graphql-ws protocol versions"],"tags":["websocket","graphql","json","serde","protocol","rust"],"backgroundTag":"invalid-message-schema","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}