hasura/graphql-engine · error · ConnectionInitError
Connection already initialized
Error message
Connection already initialized
What it means
ConnectionInitError::AlreadyInitialized is returned by the graphql-ws protocol layer when a client attempts to perform connection initialization on a WebSocket connection that has already been initialized. The graphql-transport-ws protocol allows exactly one 'connection_init' message; a second one is a protocol violation and produces this error, typically closing the socket with 4408 'Connection initialisation timeout' semantics or a server close.
Source
Thrown at v3/crates/graphql/graphql-ws/src/protocol/init.rs:120
.await?;
// Authorize the authenticated identity
let session = authorize_identity(&auth_response.identity, &headers)?;
Ok((session, headers))
}
ConnectionInitState::Initialized { .. } => {
Err(ConnectionInitError::AlreadyInitialized)
}
}
})
},
)
.await
}
/// Error types that may occur during connection initialization.
#[derive(Debug, thiserror::Error)]
pub enum ConnectionInitError {
#[error("Connection already initialized")]
AlreadyInitialized,
#[error("Invalid header name: {0}")]
InvalidHeaderName(#[from] http::header::InvalidHeaderName),
#[error("Invalid header value: {0}")]
InvalidHeaderValue(#[from] http::header::InvalidHeaderValue),
#[error("AuthError: {0}")]
Authn(#[from] AuthError),
#[error("SessionError: {0}")]
Session(#[from] SessionError),
}
impl tracing_util::TraceableError for ConnectionInitError {
fn visibility(&self) -> tracing_util::ErrorVisibility {
tracing_util::ErrorVisibility::User
}
}
/// Parses headers from a given map of strings into an `http::HeaderMap`.View on GitHub (pinned to 724551b9ae)
Solutions
- Send 'connection_init' exactly once per WebSocket connection, immediately after the socket opens, and wait for 'connection_ack' before subscribing
- On reconnect, always open a fresh WebSocket connection rather than reusing the old one
- Use a maintained client library (graphql-ws npm package) instead of a hand-rolled protocol implementation
- Add a client-side state flag (initialized=true after ack) to guard against duplicate init sends
Example fix
// before
socket.on('sub', () => socket.send(msg('connection_init', payload)));
// after
let initialized = false;
function ensureInit() {
if (!initialized) { socket.send(msg('connection_init', payload)); }
} Defensive patterns
Strategy: validation
Validate before calling
// Client: track init state per socket before sending messages
let initialized = socket.waitForMessage('connection_ack') !== null;
function send(msg) {
if (msg.type === 'connection_init' && initialized) {
throw new Error('connection already initialized; open a new socket');
}
socket.send(JSON.stringify(msg));
} Try / catch
match ws_conn.initialize(payload).await {
Err(ConnectionInitError::AlreadyInitialized) => {
// protocol violation: close 4400-series and force the client to reconnect fresh
ws_conn.close(CloseCode::PolicyViolation, "duplicate connection_init").await;
}
Err(e) => log_init_error(e),
Ok(()) => (),
} Prevention
- Send connection_init exactly once per socket and await connection_ack before subscriptions
- On reconnect, create a new WebSocket rather than re-initializing the old one
- Prefer the maintained graphql-ws client library over hand-rolled protocol code
When it happens
Trigger: A WebSocket client sends a second 'connection_init' message after the connection was already acknowledged with 'connection_ack', or re-runs an init handshake on an existing socket. Also occurs when buggy client logic re-initiates the handshake on reconnect without opening a new socket.
Common situations: Custom graphql-ws client implementations that send connection_init on every subscription instead of once per socket; reconnection logic that reuses the old socket object; race conditions where auth-refresh code re-sends init; proxy/sticky-session issues delivering one client's init to an already-initialized connection.
Related errors
- subscription are not supported over HTTP
- Invalid header name: {0}
- Expecting {} protocol
- Connection initialization timed out
- Unable to parse WebSocket message: {0}
AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28).
Data as JSON: /api/errors/d810508ca1aa9c41.
Report an issue: GitHub.