moghtech/komodo · warning

Cancelled before receive

Error message

Cancelled before receive

What it means

In lib/transport/src/websocket/tungstenite.rs recv, the read is raced against a cancellation token; if the token is cancelled before the next WebSocket message arrives, the function aborts the pending read and returns 'Cancelled before receive'. This is deliberate cooperative cancellation of a blocking receive, not a socket failure.

Solutions

  1. Handle this variant as a normal shutdown signal: exit the receive loop cleanly without treating it as a transport failure.
  2. If unexpected, audit who calls cancel() on the token (reconnect logic, shutdown hooks) and whether it fires too early.
  3. Ensure the pending read is not expected to complete after cancellation; re-issue recv on a fresh connection if work must continue.
  4. Distinguish cancellation errors from network errors when logging/metrics to avoid false alarms.

Example fix

// before
let msg = ws.recv().await?;

// after
match ws.recv().await {
    Ok(msg) => handle(msg),
    Err(e) if e.to_string() == "Cancelled before receive" => break, // shutdown, not an error
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: try-catch

Type guard

fn is_cancelled(err: &anyhow::Error) -> bool { err.to_string() == "Cancelled before receive" }

Try / catch

match ws.recv().await {
    Ok(m) => handle(m),
    Err(e) if e.to_string() == "Cancelled before receive" => break Ok(()), // expected shutdown
    Err(e) => break Err(e),
}

Prevention

When it happens

Trigger: Shutting down a task/connection while recv is (or is about to be) waiting: dropping the runtime actor, calling cancel() on the shared CancellationToken, or a supervisor aborting the connection during teardown or reconnect.

Common situations: Graceful shutdown of a service holding an open WebSocket; reconnect logic cancelling the old connection's receive loop; tests or timeouts cancelling long-idle reads.

Related errors


AI-assisted analysis of moghtech/komodo@780ac68b99 (2026-09-08). Data as JSON: /api/errors/a33d5e3daba57673. Report an issue: GitHub.

Appendix: source

Thrown at lib/transport/src/websocket/tungstenite.rs:164

      receiver,
      cancel: None,
    }
  }
}

impl WebsocketReceiver for TungsteniteWebsocketReceiver {
  type CloseFrame = CloseFrame;

  fn set_cancel(&mut self, cancel: CancellationToken) {
    self.cancel = Some(cancel);
  }

  async fn recv(&mut self) -> anyhow::Result<WebsocketMessage> {
    let fut = try_next(&mut self.receiver);
    if let Some(cancel) = &self.cancel {
      tokio::select! {
        res = fut => res,
        _ = cancel.cancelled() => Err(anyhow!("Cancelled before receive"))
      }
    } else {
      fut.await
    }
  }
}

impl TungsteniteWebsocket {
  pub async fn connect_maybe_tls_insecure(
    url: &str,
    insecure: bool,
  ) -> mogh_error::Result<(Self, HeaderValue)> {
    if insecure {
      Self::connect_tls_insecure(url).await
    } else {
      Self::connect(url).await
    }
  }

View on GitHub (pinned to 780ac68b99)