{"record":{"id":"a33d5e3daba57673","repo":"moghtech/komodo","slug":"cancelled-before-receive","errorCode":null,"errorMessage":"Cancelled before receive","messagePattern":"Cancelled before receive","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"lib/transport/src/websocket/tungstenite.rs","lineNumber":164,"sourceCode":"      receiver,\n      cancel: None,\n    }\n  }\n}\n\nimpl WebsocketReceiver for TungsteniteWebsocketReceiver {\n  type CloseFrame = CloseFrame;\n\n  fn set_cancel(&mut self, cancel: CancellationToken) {\n    self.cancel = Some(cancel);\n  }\n\n  async fn recv(&mut self) -> anyhow::Result<WebsocketMessage> {\n    let fut = try_next(&mut self.receiver);\n    if let Some(cancel) = &self.cancel {\n      tokio::select! {\n        res = fut => res,\n        _ = cancel.cancelled() => Err(anyhow!(\"Cancelled before receive\"))\n      }\n    } else {\n      fut.await\n    }\n  }\n}\n\nimpl TungsteniteWebsocket {\n  pub async fn connect_maybe_tls_insecure(\n    url: &str,\n    insecure: bool,\n  ) -> mogh_error::Result<(Self, HeaderValue)> {\n    if insecure {\n      Self::connect_tls_insecure(url).await\n    } else {\n      Self::connect(url).await\n    }\n  }","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/moghtech/komodo/blob/780ac68b992094a9fccd5fffb760e0c84fd3c3d1/lib/transport/src/websocket/tungstenite.rs#L146-L182","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Handle this variant as a normal shutdown signal: exit the receive loop cleanly without treating it as a transport failure.","If unexpected, audit who calls cancel() on the token (reconnect logic, shutdown hooks) and whether it fires too early.","Ensure the pending read is not expected to complete after cancellation; re-issue recv on a fresh connection if work must continue.","Distinguish cancellation errors from network errors when logging/metrics to avoid false alarms."],"exampleFix":"// before\nlet msg = ws.recv().await?;\n\n// after\nmatch ws.recv().await {\n    Ok(msg) => handle(msg),\n    Err(e) if e.to_string() == \"Cancelled before receive\" => break, // shutdown, not an error\n    Err(e) => return Err(e),\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"fn is_cancelled(err: &anyhow::Error) -> bool { err.to_string() == \"Cancelled before receive\" }","tryCatchPattern":"match ws.recv().await {\n    Ok(m) => handle(m),\n    Err(e) if e.to_string() == \"Cancelled before receive\" => break Ok(()), // expected shutdown\n    Err(e) => break Err(e),\n}","preventionTips":["Treat cancellation as a control-flow signal, not a failure; branch on the message before logging as error.","Centralize who owns the CancellationToken and document when cancel() fires.","Avoid cancelling while a critical read/replay is in flight; cancel only at safe points.","In tests, assert this variant is produced on shutdown to keep reconnect logic honest."],"tags":["websocket","cancellation","tokio","shutdown","rust"],"backgroundTag":"operation-cancelled","analyzedSha":"780ac68b992094a9fccd5fffb760e0c84fd3c3d1","analyzedAt":"2026-09-08T10:02:44.861Z","contentChangedAt":"2026-09-08T10:02:44.861Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}