tokio-rs/tokio · warning · io::Error

sender not available

Error message

sender not available

What it means

peek_sender_inner calls Socket::peek_sender() then converts via .as_socket(); when as_socket() returns None (the platform didn't populate a recognizable IPv4/IPv6 source), tokio returns this io::ErrorKind::Other error. The code comment notes that during testing this was only seen on macOS with a zero-sized receive buffer. It is a defensive guard for an ill-formed control message.

Source

Thrown at tokio/src/net/udp.rs:1892

    /// It is important to be aware of this when designing your application-level protocol.
    ///
    /// [packet injection attack]: https://en.wikipedia.org/wiki/Packet_injection
    pub fn try_peek_sender(&self) -> io::Result<SocketAddr> {
        self.io
            .registration()
            .try_io(Interest::READABLE, || self.peek_sender_inner())
    }

    #[inline]
    fn peek_sender_inner(&self) -> io::Result<SocketAddr> {
        self.io.try_io(|| {
            self.as_socket()
                .peek_sender()?
                // May be `None` if the platform doesn't populate the sender for some reason.
                // In testing, that only occurred on macOS if you pass a zero-sized buffer,
                // but the implementation of `Socket::peek_sender()` covers that.
                .as_socket()
                .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "sender not available"))
        })
    }

    /// Gets the value of the `SO_BROADCAST` option for this socket.
    ///
    /// For more information about this option, see [`set_broadcast`].
    ///
    /// [`set_broadcast`]: method@Self::set_broadcast
    pub fn broadcast(&self) -> io::Result<bool> {
        self.io.broadcast()
    }

    /// Sets the value of the `SO_BROADCAST` option for this socket.
    ///
    /// When enabled, this socket is allowed to send packets to a broadcast
    /// address.
    pub fn set_broadcast(&self, on: bool) -> io::Result<()> {
        self.io.set_broadcast(on)

View on GitHub (pinned to 625954f365)

Solutions

  1. Pass a non-zero buffer to peek_sender (the receive path still needs somewhere to put bytes).
  2. On macOS, use recv_from instead of peek_sender if you need the sender with a zero-byte probe.
  3. Handle io::ErrorKind::Other with this message as a soft failure — log and skip rather than tearing down the socket.
  4. Upgrade socket2/tokio to pick up any platform fixes for msg_name population.

Example fix

// before
let peer = sock.peek_sender().await?; // 'sender not available' on macOS

// after
let peer = match sock.peek_sender().await {
    Ok(p) => p,
    Err(e) if e.to_string() == "sender not available" => {
        // fall back to a real recv to learn the peer
        let mut buf = [0u8; 1];
        let (_, p) = sock.recv_from(&mut buf).await?;
        p
    }
    Err(e) => return Err(e.into()),
};
Defensive patterns

Strategy: try-catch

Validate before calling

// Pass a non-zero buffer to peek_sender; on macOS avoid zero-byte probes.
// (No pure pre-check exists; guard at the call site.)

Type guard

fn is_sender_unavailable(e: &io::Error) -> bool {
    e.kind() == io::ErrorKind::Other && e.to_string() == "sender not available"
}

Try / catch

match sock.peek_sender().await {
    Ok(p) => Ok(p),
    Err(e) if e.to_string() == "sender not available" => {
        let mut buf = [0u8; 1];
        let (_, p) = sock.recv_from(&mut buf).await?;
        Ok(p)
    }
    Err(e) => Err(e.into()),
}

Prevention

When it happens

Trigger: Calling UdpSocket::peek_sender() (or the underlying try_io) on a socket where the kernel returns a recvmsg ancillary sender that doesn't decode to an INET address. The doc note calls out macOS + zero-sized buffer as the observed cause.

Common situations: macOS only; passing a zero-length buffer to peek_sender; exotic platforms whose msg_name is malformed; ABI quirks in socket2 on specific macOS versions.

Related errors


AI-assisted analysis of tokio-rs/tokio@625954f365 (2026-08-11). Data as JSON: /api/errors/20c77af8b695fdc7. Report an issue: GitHub.