nautechsystems/nautilus_trader · error · anyhow::Error
Polymarket WebSocket handler failed: {error}
Error message
Polymarket WebSocket handler failed: {error} What it means
disconnect() aborts the feed-handler task and joins it with a 2s timeout. TaskJoinOutcome::Failed means the handler task finished on its own but returned an Err; disconnect() surfaces that handler error as 'Polymarket WebSocket handler failed: {error}'. The {error} text is the handler's own failure cause (connection, auth, parse, etc.), so the real diagnosis lives in the wrapped message.
Source
Thrown at crates/adapters/polymarket/src/websocket/client.rs:456
/// Disconnects the WebSocket connection.
pub async fn disconnect(&mut self) -> anyhow::Result<()> {
log::debug!("Disconnecting Polymarket WebSocket");
self.signal.store(true, Ordering::Relaxed);
if let Err(e) = self.cmd_tx.read().await.send(HandlerCommand::Disconnect) {
log::debug!("Failed to send disconnect (handler may already be shut down): {e}");
}
let task_result = match finish_task(
&mut self.task_handle,
std::time::Duration::from_secs(2),
std::time::Duration::from_secs(2),
)
.await
{
None | Some(TaskJoinOutcome::Completed(()) | TaskJoinOutcome::Aborted) => Ok(()),
Some(TaskJoinOutcome::Failed(error)) => Err(anyhow::anyhow!(
"Polymarket WebSocket handler failed: {error}"
)),
Some(TaskJoinOutcome::Incomplete) => Err(anyhow::anyhow!(
"Polymarket WebSocket handler did not stop after abort"
)),
};
// Invalidate after the task has stopped so any in-flight auth_tracker.succeed()
// calls from the handler cannot race with and survive the invalidation.
self.auth_tracker.invalidate();
if let Some(control) = &self.socket_control {
control.deregister();
}
log::debug!("Polymarket WebSocket disconnected");
task_result
}
/// Returns `true` if the WebSocket is actively connected.View on GitHub (pinned to 18893faf8b)
Solutions
- Read the wrapped {error} in the message — it names the handler's actual failure; fix that root cause
- Treat it as non-fatal during shutdown if you only wanted the task gone: the session is stopped either way
- If auth-related, refresh API credentials before reconnecting
- Add retries/backoff on reconnect if the handler repeatedly dies from transient network errors
Example fix
// before
client.disconnect().await?; // propagates handler's own failure
// after
if let Err(e) = client.disconnect().await {
log::warn!("disconnect reported handler failure (session stopped anyway): {e:#}");
} Defensive patterns
Strategy: try-catch
Try / catch
match client.disconnect().await {
Ok(()) => {},
Err(e) if e.to_string().starts_with("Polymarket WebSocket handler failed") => {
// handler already dead with its own error; session is stopped either way
log::warn!("handler failed during disconnect: {e:#}");
}
Err(e) => return Err(e),
} Prevention
- Always inspect the wrapped inner error — it names the handler's real failure
- Give WebSocket reads/writes in the handler explicit timeouts so errors surface predictably
- Refresh credentials before reconnecting when the wrapped error is auth-related
- Log handler exits at warn/error level so disconnect-time failures are not surprising
When it happens
Trigger: Calling disconnect() while (or after) the handler task has terminated with an error — e.g. the WebSocket stream errored out, an auth call failed, or message parsing returned a fatal error that ended the handler's run loop.
Common situations: Shutdown racing a handler that just died from a network drop; credentials revoked mid-session so the handler exits with an auth error; a malformed venue message causing a fatal parse error in the handler loop.
Related errors
- Polymarket WebSocket handler did not stop after abort
- conflicting RTDS TWAP observation topic={} symbol={} timesta
- Unsupported RTDS custom data type: {other}
- Failed to start Polymarket WebSocket handler task: {e}
- subscribe_market() requires a market-channel client (created
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/bbbe9397f832533f.
Report an issue: GitHub.