{"record":{"id":"9ec243a610cd0a0a","repo":"moghtech/komodo","slug":"connection-already-closed","errorCode":null,"errorMessage":"Connection already closed","messagePattern":"Connection already closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"lib/transport/src/websocket/mod.rs","lineNumber":95,"sourceCode":"  > {\n    MaybeWithTimeout::new(async {\n      loop {\n        match tokio::time::timeout(\n          Duration::from_secs(10),\n          self.recv_inner(),\n        )\n        .await\n        .context(\"Timed out waiting for Ping\")??\n        {\n          WebsocketMessage::Message(message) => {\n            return message.decode();\n          }\n          WebsocketMessage::Ping => continue,\n          WebsocketMessage::Close => {\n            return Err(anyhow!(\"Connection closed\"));\n          }\n          WebsocketMessage::Closed => {\n            return Err(anyhow!(\"Connection already closed\"));\n          }\n        }\n      }\n    })\n  }\n}\n\nimpl<W: Websocket> WebsocketExt for W {}\n\n/// Traits for split websocket receiver\npub trait WebsocketSender {\n  /// Streamlined pinging\n  fn ping(\n    &mut self,\n  ) -> impl Future<Output = anyhow::Result<()>> + Send;\n\n  /// Streamlined sending on bytes\n  fn send(","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/moghtech/komodo/blob/780ac68b992094a9fccd5fffb760e0c84fd3c3d1/lib/transport/src/websocket/mod.rs#L77-L113","documentation":"In the same websocket receive loop, a Closed indicator means this side already knows the socket is closed (e.g. a prior close or a failed send marked it). recv_message surfaces 'Connection already closed' instead of attempting to read from a dead socket, distinguishing local closure from a peer Close frame.","triggerScenarios":"Calling recv_message after the websocket was already closed locally or marked closed by a previous operation; calling recv concurrently after a close was processed.","commonSituations":"Continuing a receive loop after a send failed and closed the socket; double-closing a connection in cleanup code; races where one task closes while another still polls recv_message.","solutions":["Stop the receive loop when this error occurs instead of retrying on the same socket","Track connection state in the caller and never call recv_message after initiating close","Reconnect if further communication is needed","Serialize close/recv access (e.g. via a select loop in one task) to avoid racing the socket"],"exampleFix":"// before\nloop { let msg = ws.recv_message().await?; }\n// after\nloop {\n  match ws.recv_message().await {\n    Ok(m) => handle(m),\n    Err(e) if e.to_string().contains(\"closed\") => break, // stop, don't retry\n    Err(e) => return Err(e),\n  }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"fn is_closed_error(e: &anyhow::Error) -> bool {\n  let s = e.to_string();\n  s == \"Connection closed\" || s == \"Connection already closed\"\n}","tryCatchPattern":"match ws.recv_message().await {\n  Ok(m) => handle(m),\n  Err(e) if e.to_string() == \"Connection already closed\" => break, // stop looping\n  Err(e) => return Err(e),\n}","preventionTips":["Don't call recv_message after closing or observing a failed send","Concentrate socket close/recv handling in a single task to avoid races","Represent connection state (Open/Closed) in the caller and gate calls on it","Break out of receive loops on any 'closed' error rather than retrying"],"tags":["websocket","network","connection-closed","state-management"],"backgroundTag":"connection-closed","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"}