vi/websocat · error
Assertion failed 193913
Error message
Assertion failed 193913
What it means
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.
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.
Example fix
// before
let mut p = self.0.borrow_mut();
match p.state.take().expect("Assertion failed 193913") {
// after
// restore state even on early return / error paths
let mut p = self.0.borrow_mut();
let st = p.state.take().expect("Assertion failed 193913");
let result = match st {
UdpPeerState::ConnectMode => {
p.state = Some(UdpPeerState::ConnectMode);
p.s.send2(buf)
}
other @ _ => {
p.state = Some(other);
/* ... */
}
}; Defensive patterns
Strategy: type-guard
Validate before calling
// before writing, confirm the state slot is populated
if peer.state_is_none() {
return Err("udp peer state missing: previous operation did not restore state");
} Type guard
fn can_write(handle: &UdpPeerHandle) -> bool {
handle.0.borrow().state.is_some()
} Try / catch
// expect() panics are not catchable; guard at the call site
if !can_write(&handle) {
return Err(Error::InvalidState("udp peer state not available for write"));
}
handle.write(buf)?; Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- assertion failed 1425
- write zero byte into writer
- lint should have caught the missing pkcs12_der option
- Time went backwards
- Nowhere to connect it
AI-assisted analysis of vi/websocat@3a3574cd2f (2026-09-12).
Data as JSON: /api/errors/0c8b06cff89da7f3.
Report an issue: GitHub.
Appendix: source
Thrown at src/net_peer.rs:460
UdpPeerState::WaitingForAddress((cmpl, pollster)) => match p.s.recv_from2(buf) {
Ok((ret, addr)) => {
p.state = Some(UdpPeerState::HasAddress(addr));
let _ = cmpl.send(());
Ok(ret)
}
Err(e) => {
p.state = Some(UdpPeerState::WaitingForAddress((cmpl, pollster)));
Err(e)
}
},
}
}
}
impl Write for UdpPeerHandle {
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
let mut p = self.0.borrow_mut();
match p.state.take().expect("Assertion failed 193913") {
UdpPeerState::ConnectMode => {
p.state = Some(UdpPeerState::ConnectMode);
p.s.send2(buf)
}
UdpPeerState::HasAddress(a) => {
if p.oneshot_mode {
p.state = Some(UdpPeerState::WaitingForAddress(channel()));
} else {
p.state = Some(UdpPeerState::HasAddress(a));
}
p.s.send_to2(buf, &a)
}
UdpPeerState::WaitingForAddress((cmpl, mut pollster)) => {
let _ = pollster.poll(); // register wakeup
p.state = Some(UdpPeerState::WaitingForAddress((cmpl, pollster)));
wouldblock()
}
}View on GitHub (pinned to 3a3574cd2f)