hasura/graphql-engine · error · Error
poller with operation_id {} already exists
Error message
poller with operation_id {} already exists What it means
The subscribe handler detected an internal conflict: a poller for the given `OperationId` already exists when a new one was being registered. Each subscription operation should map to exactly one poller; a duplicate registration attempt triggers this error.
Source
Thrown at v3/crates/graphql/graphql-ws/src/protocol/subscribe.rs:21
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>,
operation_id: OperationId,
payload: lang_graphql::http::RawRequest,View on GitHub (pinned to 724551b9ae)
Solutions
- Generate a unique operation id (e.g. UUID) for every subscribe message
- Send `stop`/`complete` for the previous operation before re-subscribing with the same id
- Make subscribe handlers idempotent by treating a duplicate as a stop+resubscribe
- Check for duplicate message delivery (network retries, buggy reconnect logic)
Example fix
// before const opId = 'my-query'; // after const opId = crypto.randomUUID();
Defensive patterns
Strategy: validation
Validate before calling
const active = new Set();
function nextOpId(){ let id; do { id = crypto.randomUUID(); } while(active.has(id)); active.add(id); return id; } Try / catch
catch (e) { if (/poller with operation_id .* already exists/.test(String(e))) { stop(oldId); resubscribeWithNewId(); } } Prevention
- Use unique ids per subscription
- Send stop before re-subscribing
- Make subscribe idempotent server-side
When it happens
Trigger: A client reusing the same operation id for a new `subscribe` message without first completing/stopping the previous subscription; internal code paths registering a poller twice for the same OperationId (e.g. after a retry or duplicate message delivery).
Common situations: Client generating colliding operation ids (e.g. a constant id instead of unique ids); at-least-once message delivery causing duplicate subscribes; retry logic resending subscribe without a unique id.
Related errors
- graphql-ws protocol is not initialized
- error in pre-parse plugin: {0}
- error in executing pre-response plugins: {0}
- subscription are not supported over HTTP
- Connection already initialized
AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28).
Data as JSON: /api/errors/dbd5db6b95ba8057.
Report an issue: GitHub.