{"record":{"id":"0c8b06cff89da7f3","repo":"vi/websocat","slug":"assertion-failed-193913","errorCode":null,"errorMessage":"Assertion failed 193913","messagePattern":"Assertion failed 193913","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/net_peer.rs","lineNumber":460,"sourceCode":"            UdpPeerState::WaitingForAddress((cmpl, pollster)) => match p.s.recv_from2(buf) {\n                Ok((ret, addr)) => {\n                    p.state = Some(UdpPeerState::HasAddress(addr));\n                    let _ = cmpl.send(());\n                    Ok(ret)\n                }\n                Err(e) => {\n                    p.state = Some(UdpPeerState::WaitingForAddress((cmpl, pollster)));\n                    Err(e)\n                }\n            },\n        }\n    }\n}\n\nimpl Write for UdpPeerHandle {\n    fn write(&mut self, buf: &[u8]) -> IoResult<usize> {\n        let mut p = self.0.borrow_mut();\n        match p.state.take().expect(\"Assertion failed 193913\") {\n            UdpPeerState::ConnectMode => {\n                p.state = Some(UdpPeerState::ConnectMode);\n                p.s.send2(buf)\n            }\n            UdpPeerState::HasAddress(a) => {\n                if p.oneshot_mode {\n                    p.state = Some(UdpPeerState::WaitingForAddress(channel()));\n                } else {\n                    p.state = Some(UdpPeerState::HasAddress(a));\n                }\n                p.s.send_to2(buf, &a)\n            }\n            UdpPeerState::WaitingForAddress((cmpl, mut pollster)) => {\n                let _ = pollster.poll(); // register wakeup\n                p.state = Some(UdpPeerState::WaitingForAddress((cmpl, pollster)));\n                wouldblock()\n            }\n        }","sourceCodeStart":442,"sourceCodeEnd":478,"githubUrl":"https://github.com/vi/websocat/blob/3a3574cd2f5d17857d87f3982e72c3ede159dde0/src/net_peer.rs#L442-L478","documentation":"This panic comes from p.state.take().expect(\"Assertion failed 193913\") in UdpPeerHandle::write. The handle stores UdpPeerState in an Option and take()s it so the borrowed state can be temporarily moved out; a None means the state slot was empty when write() ran. That happens when write is called re-entrantly while a previous operation still holds the taken state, or the handle was left in a half-updated state by an earlier aborted call.","triggerScenarios":"Calling write() on a UdpPeerHandle twice without the first call restoring state (re-entrant write during the previous write, e.g. from a nested poll/flush of an adapter that invokes Write twice), or using the handle after an internal code path consumed the state without putting it back.","commonSituations":"Wrapping UdpPeerHandle in a BufWriter or async adapter whose flush path calls write() again while the original write is in progress; sharing the handle across threads so two writers race; a code change that returns early from a match arm after take() without restoring p.state.","solutions":["Audit every code path that takes p.state and ensure it is restored (Some(...)) before write() returns, including error paths.","Do not call write() re-entrantly; if wrapping in BufWriter, write to the buffer, not through nested poll paths that re-enter.","Synchronize access: UdpPeerHandle uses RefCell (borrow_mut), so do not share it across threads; use one writer at a time.","Replace expect with a proper error/panic message that names the re-entrancy cause to speed up future debugging."],"exampleFix":"// before\nlet mut p = self.0.borrow_mut();\nmatch p.state.take().expect(\"Assertion failed 193913\") {\n// after\n// restore state even on early return / error paths\nlet mut p = self.0.borrow_mut();\nlet st = p.state.take().expect(\"Assertion failed 193913\");\nlet result = match st {\n    UdpPeerState::ConnectMode => {\n        p.state = Some(UdpPeerState::ConnectMode);\n        p.s.send2(buf)\n    }\n    other @ _ => {\n        p.state = Some(other);\n        /* ... */\n    }\n};","handlingStrategy":"type-guard","validationCode":"// before writing, confirm the state slot is populated\nif peer.state_is_none() {\n    return Err(\"udp peer state missing: previous operation did not restore state\");\n}","typeGuard":"fn can_write(handle: &UdpPeerHandle) -> bool {\n    handle.0.borrow().state.is_some()\n}","tryCatchPattern":"// expect() panics are not catchable; guard at the call site\nif !can_write(&handle) {\n    return Err(Error::InvalidState(\"udp peer state not available for write\"));\n}\nhandle.write(buf)?;","preventionTips":["Never call write() re-entrantly on the same UdpPeerHandle (e.g. inside a nested flush).","Always restore p.state = Some(...) after take(), including on error paths.","Do not share a RefCell-based handle across threads.","Wrap writes in a helper that checks state presence and returns an error instead of panicking."],"tags":["rust","panic","udp","reentrancy"],"backgroundTag":"internal-invariant-violation","analyzedSha":"3a3574cd2f5d17857d87f3982e72c3ede159dde0","analyzedAt":"2026-09-12T15:14:27.766Z","contentChangedAt":"2026-09-12T15:14:27.766Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}