hasura/graphql-engine · error · ConnectionTimeOutError
Connection initialization timed out
Error message
Connection initialization timed out
What it means
The graphql-ws server starts a timer when a WebSocket connection is opened and expects the client to send the ConnectionInit message within the configured connection initialization window. If no init message arrives in time, the server gives up and reports this timeout error and closes the socket. This protects the server from idle or half-open connections occupying resources.
Source
Thrown at v3/crates/graphql/graphql-ws/src/websocket/tasks.rs:19
use super::types;
use crate::metrics::WebSocketMetrics;
use crate::protocol;
use axum::extract::ws;
use futures_util::{SinkExt, StreamExt};
use tokio::time::{Duration, timeout};
/// Enum to represent whether the loop should continue or break.
#[derive(PartialEq)]
enum BreakLoop {
/// Indicates the loop should break.
Break,
/// Indicates the loop should continue.
Dont,
}
/// Error type for connection timeouts during WebSocket initialization.
#[derive(thiserror::Error, Debug)]
#[error("Connection initialization timed out")]
pub(crate) struct ConnectionTimeOutError;
impl tracing_util::TraceableError for ConnectionTimeOutError {
fn visibility(&self) -> tracing_util::ErrorVisibility {
tracing_util::ErrorVisibility::User
}
}
/// Checks if the graphql-ws protocol is initialized within the specified timeout duration.
/// Ref: <https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md#connectioninit>
pub(crate) async fn verify_connection_init<M: Send>(
connection: types::Connection<M>,
timeout_duration: Duration,
parent_span_link: tracing_util::SpanLink,
) -> Result<(), ConnectionTimeOutError> {
let tracer = tracing_util::global_tracer();
tracer
.new_trace_async_with_link(View on GitHub (pinned to 724551b9ae)
Solutions
- Ensure your client sends connection_init immediately after the socket opens (graphql-ws clients do this automatically when a payload/callback is provided)
- Increase the server's connection init timeout configuration if clients are slow to initialize
- Check for intermediaries that delay or buffer the first WebSocket frame
Example fix
// before (client)
const ws = new WebSocket(url, 'graphql-ws');
// ...subscribe without initializing
// after
import { createClient } from 'graphql-ws';
const client = createClient({ webSocketImpl: ws, connectionInit: { authorization: token } }); Defensive patterns
Strategy: retry
Validate before calling
// Ensure connectionInit is provided so the client sends connection_init immediately
const client = createClient({ webSocketImpl: ws, connectionInit: {{}} }); Prevention
- Always configure a connectionInit payload/callback in your graphql-ws client
- Keep client startup work off the socket-open critical path
- Tune server connection_init_timeout above worst-case client startup latency
When it happens
Trigger: A client opens the WebSocket but never sends the 'connection_init' message (no payload or a client bug), or network/latency causes it to arrive after the configured connection_init_timeout window elapses.
Common situations: Custom WebSocket clients that only subscribe without initializing; heavy startup work on the client delaying the init frame; misconfigured very short connection init timeout on the server; NAT/proxy idle handling delaying the first frame.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- subscription are not supported over HTTP
- Connection already initialized
- Invalid header name: {0}
- Expecting {} protocol
- Unable to parse WebSocket message: {0}
AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28).
Data as JSON: /api/errors/4898fe50a2b88b55.
Report an issue: GitHub.