hasura/graphql-engine · error · Error
graphql-ws protocol is not initialized
Error message
graphql-ws protocol is not initialized
What it means
Thrown by the graphql-ws subscribe handler when a `subscribe` message arrives before the protocol has been initialized via `connection_init`. The protocol state machine requires a successful initialization before any subscriptions can be created.
Source
Thrown at v3/crates/graphql/graphql-ws/src/protocol/subscribe.rs:19
use super::types::{ConnectionInitState, OperationId, ServerMessage};
use crate::metrics::WebSocketMetrics;
use crate::poller;
use crate::websocket::types as ws;
use ::pre_response_plugin::execute::PreResponsePluginResponse;
use axum::http;
use blake2::{Blake2b, Digest};
use engine_types::ExposeInternalErrors;
use graphql_frontend::{ExecuteQueryResult, RootFieldResult, process_response};
use graphql_ir::RequestPlan;
use hasura_authn_core::Session;
use indexmap::IndexMap;
use nonempty::NonEmpty;
use pre_parse_plugin::execute as pre_parse_plugin;
use pre_response_plugin::execute as pre_response_plugin;
#[derive(thiserror::Error, Debug)]
enum Error {
#[error("graphql-ws protocol is not initialized")]
NotInitialized,
#[error("poller with operation_id {} already exists", operation_id.0)]
PollerAlreadyExists { operation_id: OperationId },
#[error("error in pre-parse plugin: {0}")]
PreParsePlugin(#[from] pre_parse_plugin::Error),
}
impl tracing_util::TraceableError for Error {
fn visibility(&self) -> tracing_util::ErrorVisibility {
tracing_util::ErrorVisibility::User
}
}
/// Handles the subscription message from the client.
/// It either starts a new poller or sends a close message if the poller with given operation_id already exists.
pub async fn handle_subscribe<M: WebSocketMetrics>(
client_address: std::net::SocketAddr,
connection: ws::Connection<M>,View on GitHub (pinned to 724551b9ae)
Solutions
- Ensure the client sends `connection_init` first and waits for `connection_ack` before subscribing
- If reconnecting, re-run the full handshake instead of reusing the old socket state
- Check that connection_init did not fail (auth errors often cause silent non-initialization)
- Add client-side state tracking so subscribe is only called after ack
Example fix
// before
ws.onopen = () => ws.send(JSON.stringify({type:'subscribe',...}));
// after
let ready = false;
ws.onopen = () => ws.send(JSON.stringify({type:'connection_init',payload:{...}}));
ws.onmessage = (m) => { if (JSON.parse(m.data).type==='connection_ack') ready = true; };
ws.sendSubscribe = () => { if (ready) ws.send(JSON.stringify({type:'subscribe',...})); }; Defensive patterns
Strategy: validation
Validate before calling
let initialized = false;
function assertInitialized(){ if(!initialized) throw new Error('connection not initialized'); } Try / catch
catch (e) { if (String(e).includes('not initialized')) { sendInit(); /* wait ack then resubscribe */ } } Prevention
- Always wait for connection_ack before subscribing
- Re-run the handshake after any reconnect
- Treat subscribe-before-ack as a client bug and fix state ordering
When it happens
Trigger: Sending a `subscribe` message on a WebSocket before sending (or before the server has acknowledged) a `connection_init` message; also when initialization failed silently and the client proceeds to subscribe.
Common situations: Client library that skips connection_init; race where the client sends subscribe before processing the connection_ack; reconnect logic that reuses stale state and skips re-initialization.
Related errors
- poller with operation_id {} already exists
- subscription are not supported over HTTP
- Connection already initialized
- Invalid header value: {0}
- AuthError: {0}
AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28).
Data as JSON: /api/errors/f265c80556e17aa2.
Report an issue: GitHub.