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

  1. Audit every code path that takes p.state and ensure it is restored (Some(...)) before write() returns, including error paths.
  2. Do not call write() re-entrantly; if wrapping in BufWriter, write to the buffer, not through nested poll paths that re-enter.
  3. Synchronize access: UdpPeerHandle uses RefCell (borrow_mut), so do not share it across threads; use one writer at a time.
  4. 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

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


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)