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

  1. Send 'connection_init' exactly once per WebSocket connection, immediately after the socket opens, and wait for 'connection_ack' before subscribing
  2. On reconnect, always open a fresh WebSocket connection rather than reusing the old one
  3. Use a maintained client library (graphql-ws npm package) instead of a hand-rolled protocol implementation
  4. 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

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


AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28). Data as JSON: /api/errors/d810508ca1aa9c41. Report an issue: GitHub.